---
url: /config/features.md
---
# Xray 特性详解

Xray 具备以下特性：

* [XTLS 深度剖析](xtls.md)
* [Fallback 回落](fallback.md)
* [Browser Dialer](browser_dialer.md)
* [环境变量](env.md)
* [多文件配置](multiple.md)

---

---
url: /config/features/xtls.md
---
# XTLS 深度剖析

> **XTLS 是 Xray 的原创黑科技, 也是使 Xray 性能一骑绝尘的核心动力**

---

---
url: /config/features/fallback.md
---
# Fallback 回落

> **Fallback 是 Xray 的最强大功能之一, 可有效防止主动探测, 自由配置常用端口多服务共享**

fallback 为 Xray 提供了高强度的防主动探测性, 并且具有独创的首包回落机制.

fallback 也可以将不同类型的流量根据 path 进行分流, 从而实现一个端口, 多种服务共享.

目前您可以在使用 VLESS 或者 trojan 协议时, 通过配置 fallbacks 来使用回落这一特性, 并且创造出非常丰富的组合玩法.

## fallbacks 配置

```json
  "fallbacks": [
    {
      "dest": 80
    }
  ]
```

> `fallbacks`: \[ [FallbackObject](#fallbackobject) ]

一个数组，包含一系列强大的回落分流配置。

### FallbackObject

```json
{
  "name": "",
  "alpn": "",
  "path": "",
  "dest": 80,
  "xver": 0
}
```

**`fallbacks` 是一个数组，这里是其中一个子元素的配置说明。**

`fallbacks` 项是可选的，只能用于 TCP+TLS 传输组合

* 该项有子元素时，[Inbound TLS](../transport.md#tlsobject) 需设置 `"alpn":["http/1.1"]`。\*\*

通常，你需要先设置一组 `alpn` 和 `path` 均省略或为空的默认回落，然后再按需配置其它分流。

VLESS 会把 TLS 解密后首包长度 < 18 或协议版本无效、身份认证失败的流量转发到 `dest` 指定的地址。

其它传输组合必须删掉 `fallbacks` 项或所有子元素，此时也不会开启 Fallback，VLESS 会等待读够所需长度，协议版本无效或身份认证失败时，将直接断开连接。

> `name`: string

尝试匹配 TLS SNI(Server Name Indication)，空为任意，默认为 ""

> `alpn`: string

尝试匹配 TLS ALPN 协商结果，空为任意，默认为 ""

有需要时，VLESS 才会尝试读取 TLS ALPN 协商结果，若成功，输出 info `realAlpn =` 到日志。
用途：解决了 Nginx 的 h2c 服务不能同时兼容 http/1.1 的问题，Nginx 需要写两行 listen，分别用于 1.1 和 h2c。
注意：fallbacks alpn 存在 `"h2"` 时，[Inbound TLS](../transport.md#tlsobject) 需设置 `"alpn":["h2","http/1.1"]`，以支持 h2 访问。

::: tip
Fallback 内设置的 `alpn` 是匹配实际协商出的 ALPN，而 Inbound TLS 设置的 `alpn` 是握手时可选的 ALPN 列表，两者含义不同。
:::

> `path`: string

尝试匹配首包 HTTP PATH，空为任意，默认为空，非空则必须以 `/` 开头，不支持 h2c。

智能：有需要时，VLESS 才会尝试看一眼 PATH（不超过 55 个字节；最快算法，并不完整解析 HTTP），若成功，输出 INFO 日志 `realPath =`。
用途：分流其它 inbound 的 WebSocket 流量或 HTTP 伪装流量，没有多余处理、纯粹转发流量，理论性能比 Nginx 更强。

注意：**fallbacks 所在入站本身必须是 TCP+TLS**，这是分流至其它 WS 入站用的，被分流的入站则无需配置 TLS。

> `dest`: string | number

决定 TLS 解密后 TCP 流量的去向，目前支持两类地址：（该项必填，否则无法启动）

1. TCP，格式为 `"addr:port"`，其中 addr 支持 IPv4、域名、IPv6，若填写域名，也将直接发起 TCP 连接（而不走内置的 DNS）。
2. Unix domain socket，格式为绝对路径，形如 `"/dev/shm/domain.socket"`，可在开头加 `@` 代表 [abstract](https://www.man7.org/linux/man-pages/man7/unix.7.html)，`@@` 则代表带 padding 的 abstract。

若只填 port，数字或字符串均可，形如 `80`、`"80"`，通常指向一个明文 http 服务（addr 会被补为 `"localhost"`）。

注：v25.7.26 后才将只包含 port 的 dest 指向 localhost, 在此之前都是 127.0.0.1. 修改后实际目标很可能是 ::1 网上抄的部分模板的 webserver 可能也监听 ::1 的同时却只允许 127 进入或者应用 proxy protocol，这可能会导致行为不同。

> `xver`: number

发送 [PROXY protocol](https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt)，专用于传递请求的真实来源 IP 和端口，填版本 1 或 2，默认为 0，即不发送。若有需要建议填 1。

目前填 1 或 2，功能完全相同，只是结构不同，且前者可打印，后者为二进制。Xray 的 TCP 和 WS 入站均已支持接收 PROXY protocol。

::: warning
若你正在 [配置 Nginx 接收 PROXY protocol](https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/#configuring-nginx-to-accept-the-proxy-protocol)，除了设置 proxy\_protocol 外，还需设置 set\_real\_ip\_from，否则可能会出问题。
:::

### 补充说明

* 将匹配到最精确的子元素，与子元素的排列顺序无关。若配置了几个 alpn 和 path 均相同的子元素，则会以最后的为准。
* 回落分流均是解密后 TCP 层的转发，而不是 HTTP 层，只在必要时检查首包 PATH。
* 您可以查看更多的关于 Fallbacks 的使用技巧和心得
  * [Fallbacks 功能简析](../../document/level-1/fallbacks-lv1)

## Fallbacks 设计理论&#x20;

---

---
url: /config/features/browser_dialer.md
---
# Browser Dialer

## 背景

通过 uTLS，Xray 可以模拟主流浏览器的 TLS 握手指纹（具体参见 TLS 中 `fingerprint` 选项）。但是仍然不能保证在任意时刻 uTLS 模拟浏览器行为完全一致。

对此 [浏览器转发（browser dialer）](https://github.com/v2ray/discussion/issues/754#issuecomment-647934994)应运而生。用户在自己的浏览器中打开一个页面至 `localhost:8080`，这个页面利用原生 JS 充当 Xray 的网络栈，与代理服务端建立 TLS，HTTP 连接。

这个方法简洁的实现了真实的浏览器的 TLS 指纹、行为特征。最大程度抗检测与抗封锁。

不过目前的浏览器转发有以下缺点：

* 用户需要手动开浏览器
* 浏览器发出的连接必须直连 使用 tun 的用户需要特别注意容易形成死循环
* 浏览器只能发出 HTTP 连接 所以目前仅支持 [WebSocket](../transports/websocket.md) 与 [XHTTP](https://github.com/XTLS/Xray-core/discussions/4113) 传输方式
* 当浏览器从 `localhost:8080` 页面连接至代理服务端，需要考虑 [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
* 因为中间经过 JS 处理数据，会有一些性能损耗
* 不能使用自定义 SNI 或者 Host，也就是说 `SNI == host == address`。自定义 HTTP 头以及其它 `tlsSettings` 项会被忽略

## 配置方法

1. 准备一份 WebSocket 或 XHTTP 配置，注意 address 必须填域名，若需要指定 IP，请配置 DNS 或系统 hosts
2. 使用环境变量启动 Xray `XRAY_BROWSER_DIALER=127.0.0.1:8080`。Windows 上命令为 `set XRAY_BROWSER_DIALER=127.0.0.1:8080` Linux 上命令为 `XRAY_BROWSER_DIALER=127.0.0.1:8080 ./xray -c config.json`
3. 确保浏览器直连（或者在路由中将服务端地址直接由 `freedom` 发出），打开页面 `localhost:8080`，还可以 `F12` 看 `Console` 和 `Network`
4. 浏览器会限制发出的连接数，所以建议开启 `Mux.Cool`

## 内部通信机制

* Xray 监听地址端口 `http://127.0.0.1:8080`，作为 HTTP 服务，浏览器访问地址，加载网页中的 JS。
* JS 主动向 `http://127.0.0.1:8080` 建立 WebSocket 连接，成功后，Xray 将连接发给 channel。
* 需要建立连接时，Xray 从 channel 接收一个可用的连接，并发送目标 URL 和可选的 early data。
* JS 成功连接到目标后告知 Xray，并继续用这个 conn 全双工双向转发数据，连接关闭行为同步。
* 连接使用后就会被关闭，但 JS 会确保始终有新空闲连接可用。

## WebSocket

根据浏览器的需求，对 early data 机制进行了如下调整：

* 服务端响应头会带有请求的 `Sec-WebSocket-Protocol`，这也初步混淆了 WSS 握手响应的长度特征。
* 用于浏览器的 early data 编码是 `base64.RawURLEncoding` 而不是 `StdEncoding`，服务端做了兼容。
* 此外，由于 [Xray-core#375](https://github.com/XTLS/Xray-core/pull/375) 推荐 `?ed=2048`，这个 PR 顺便将服务端一处 `MaxHeaderBytes` 扩至了 4096。 ~~（虽然好像不改也没问题）~~

## XHTTP

[XHTTP](https://github.com/XTLS/Xray-core/discussions/4113) 本身支持 QUIC，如果想使用浏览器自己的 QUIC 网络栈，Chrome 可以在 `chrome://flags` 中设定。其它浏览器也有相关选项。

原理上说 `tlsSettings` 项会被忽略，使用哪个 HTTP 版本将完全由浏览器决定。

---

---
url: /config/features/env.md
---
# 环境变量

Xray 提供以下环境变量以供修改 Xray 的一些底层配置。

## 资源文件路径

* 名称：`xray.location.asset` 或 `XRAY_LOCATION_ASSET`。
* 默认值：特定 [FHS](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard) 目录或 Xray 文件同路径。

这个环境变量指定了一个文件夹位置，这个文件夹应当包含 geoip.dat 和 geosite.dat 文件。
若无指定变量值，程序将会按以下顺序寻找资源文件：

```
./
/usr/local/share/xray
/usr/share/xray
```

## 配置文件位置

* 名称：`xray.location.config` 或 `XRAY_LOCATION_CONFIG`。
* 默认值：和 Xray 文件同路径。

这个环境变量指定了一个文件夹位置，这个文件夹应当包含 config.json 文件。

## 多配置目录

* 名称：`xray.location.confdir` 或 `XRAY_LOCATION_CONFDIR`。
* 默认值：`""`。

这个目录内的 `.json` 文件会按文件名顺序读取，作为多配置选项。

此项优先级低于启动参数 `confdir`。

## 严格 JSON 解析器

* 名称：`xray.json.strict` 或 `XRAY_JSON_STRICT`。
* 默认值：`false`。

默认情况下，Xray 在启动时会使用自定义的 JSON 解析器（该解析器会从配置中剔除注释及其他非标准字符）。如果你确认自己的配置文件严格遵循 JSON 标准（RFC8259），可以启用此选项以使用标准 JSON 解析器，在配置文件极大时（几十 MB 以上）可以提升其解析速度。

## 其它可用的配置

* xray.location.plugin

* xray.location.tool

* xray.location.cert

* xray.buf.readv

* xray.buf.splice

* xray.vmess.padding

* xray.cone.disabled

* xray.ray.buffer.size

* xray.browser.dialer

* xray.xudp.show

* xray.xudp.basekey

这些选项对有特殊需求的用户开放，您可以阅读源代码来发现其用途。~PR Welcome~

---

---
url: /config/features/multiple.md
---
# 多文件配置

Xray 程序支持使用多个配置文件。

多配置文件的主要作用在于分散不同作用模块配置，便于管理和维护。

该功能主要考虑是为了丰富 Xray 的生态链，比如对于 GUI 的客户端，一般只实现节点选择等固定的功能，对于太复杂的配置难以图形化实现；只需留一个 `confdir` 的自定义配置目录供配置复杂的功能；对于服务器的部署脚本，只需往 `confdir` 添加文件即可实现配置多种协议。

## 多文件启动

::: tip
启动信息中会提示依次读入的每个配置文件，留意启动信息是否符合你预设的顺序。可以在每个文件名前面增加前缀数字的方式控制顺序。如 `01_文件名`, `02_文件名`，数字越大排序越靠后。
:::

```shell
$ xray run -confdir /etc/xray/confs
```

也可使用 `Xray.location.confdir` 或 `Xray_LOCATION_CONFDIR` 指定 `confdir`。

参数 `-confdir` 的作用优先于环境变量，如果参数指定了有效的目录则不再读取环境变量中的路径。

## 规则说明

### 普通对象（`{}`）

顶级对象后者覆盖或补充前者

### 数组（`[]`）

在 json 配置中的 `inbounds` 和 `outbounds` 是数组结构，他们有特殊的规则：

* 查找原有 `tag` 相同的元素进行覆盖；若无法找到：
  * 对于 inbounds，添加至最后（inbounds 内元素顺序无关）
  * 对于 outbounds，添加至最前（outbounds 默认首选出口）；但如果文件名含有 tail（大小写均可），添加至最后。

## 配置例子

假设 `confs` 文件夹下有以下三个配置文件。

* 01.json

```json
{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks",
      "protocol": "socks",
      "listen": "0.0.0.0",
      "port": 8888
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom"
    }
  ]
}
```

* 02.json

```json
{
  "log": {
    "loglevel": "debug"
  },
  "inbounds": [
    {
      "tag": "socks",
      "protocol": "socks",
      "listen": "127.0.0.1",
      "port": 1080
    }
  ],
  "outbounds": [
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ]
}
```

* 03\_tail.json

```json
{
  "outbounds": [
    {
      "tag": "direct2",
      "protocol": "freedom"
    }
  ]
}
```

三个配置将会合并为：

```json
{
  "log": {
    "loglevel": "debug" // 顶级对象覆盖前者
  },
  "inbounds": [
    {
      "tag": "socks", // tag 相同时覆盖前者
      "protocol": "socks",
      "listen": "127.0.0.1",
      "port": 1080
    }
  ],
  "outbounds": [
    {
      "tag": "block", // outbounds 添加至最前
      "protocol": "blackhole"
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "direct2", // 03_tail.json 文件名中包含 tail 关键字，添加至最后
      "protocol": "freedom"
    }
  ]
}
```

::: tip
可以使用 `xray run -confdir=./confs -dump` 命令查看合并后的配置。但是因为 core 内部使用 protobuf 数据格式，所以 `-dump` 选项输出的配置格式会有所不同。
:::

---

---
url: /config.md
---
# 配置文件

> **这个章节将告诉您所有的 Xray 配置细节，掌握这些内容，在您手中 Xray 将发挥更大威力。**

## 概述

Xray 的配置文件为 json 格式, 客户端和服务端的配置格式没有区别, 只是实际的配置内容不一样。
形式如下:

```json
{
  "version": {},
  "log": {},
  "api": {},
  "dns": {},
  "routing": {},
  "policy": {},
  "inbounds": [],
  "outbounds": [],
  "transport": {},
  "stats": {},
  "fakedns": {},
  "metrics": {},
  "observatory": {},
  "burstObservatory": {},
  "geodata": {}
}
```

::: warning
如果你刚接触 Xray, 您可以先点击查看[快速入门中的配置运行](../document/install.md), 学习最基本的配置方式, 然后查看本章节内容以掌握所有 Xray 的配置方式。
:::

:::: tip 让 AI 更可靠地协助你配置 Xray
::: details 点击查看可复制的提示词
无论你是想让 AI 直接生成配置，还是想咨询具体配置问题，
都建议你**在对话一开始就先把下面这段话发给 AI**：

````markdown
# 角色

你是一个专门帮助用户编写和理解 Xray-core 配置的助手。

你的任务是基于官方文档，帮助我解释配置项，或生成可使用的 Xray-core 配置文件。

# 唯一依据

请使用以下 Xray-core 官方全量文档作为唯一依据：

https://xtls.github.io/llms-full.txt

在回答任何配置相关问题前，请先打开并阅读该文档中的相关部分。

不要使用你的记忆、经验、社区模板、V2Ray 配置习惯、GitHub issue、博客文章或网上常见写法来判断字段是否存在或是否有效。

# 最重要的规则

不要编造配置字段。

只能使用官方文档中明确提到的字段、取值、默认值、限制条件和配置结构。

如果官方文档没有提到某个字段、取值、默认值、限制条件或组合方式，请直接回答：

“文档未提及，不能确认。”

不要猜测，不要补全，不要为了让配置看起来完整而添加没有文档依据的字段。

# 如果无法访问文档

如果你无法打开或读取官方文档链接，请直接说明：

“我无法访问官方文档链接，因此不能保证不产生幻觉。请手动下载 https://xtls.github.io/llms-full.txt 并上传给我，我会只基于上传文档回答。”

在无法访问官方文档时，不要根据记忆生成 Xray-core 配置，也不要根据记忆解释配置细节。

# 回答流程

回答任何配置相关问题时，请遵循以下流程：

1. 先阅读官方文档中的相关部分。
2. 找出相关的配置对象、字段、取值和限制条件。
3. 只基于文档明确确认的内容回答。
4. 如果某部分文档没有确认，请标记为“文档未提及，不能确认”。

生成配置时，请遵循以下流程：

1. 先确认你准备使用哪些字段。
2. 只用官方文档确认过的字段生成配置。
3. 输出前检查最终配置，删除任何无法由文档确认的字段。
4. 如果我的需求中有文档无法确认的部分，请放到“未确认内容”。

# 输出格式

默认输出 JSONC，也就是允许带 `//` 中文注释的 JSON 风格配置。

注释应该帮助普通用户理解：

- 这个字段做什么；
- 用户是否需要修改；
- 修改时要注意什么。

注释不能引入官方文档没有确认的功能。

如果我明确要求“纯 JSON”，请输出不带注释的合法 JSON。

不要使用 `_comment` 字段写注释，除非官方文档明确说支持这个字段。

# 生成配置时的回答格式

请使用以下格式：

## 文档依据

简要列出本次使用到的官方文档中的配置对象和关键字段。

## 配置文件

```jsonc
{
  // 在这里写配置
}
```

## 关键说明

解释我最需要修改或注意的字段。

## 未确认内容

列出我的需求中官方文档没有确认的部分。

如果没有未确认内容，请写：

“无。”

# 解释配置项时的回答格式

请使用以下格式：

## 结论

直接解释这个字段或配置对象的作用。

## 文档依据

说明它属于哪个官方文档中的配置对象，以及文档明确确认了什么。

## 注意事项

只写官方文档明确提到的限制、默认值、可选值或组合规则。

## 文档未提及

列出我的问题中官方文档没有确认的部分。
````

:::
::::

## 基础配置模块

> version

可选，控制该 config 可以运行的版本，当分享 config 时防止在不期望的客户端版本意外运行，运行时客户端将会检查当前版本是否匹配该要求。

```json
"version": {
    "min": "25.8.3",
    "max": ""
}
```

`min` 与 `max` 均为可选，不设置或留空代表不设限。不需要是实际存在的版本，只要符合 Xray 版本号 x.y.z 的语法即可。

25.8.3 是 Xray 添加该功能的版本，设置低于这个的版本没有任何意义(旧版本不会检查)

> log:[LogObject](./log.md)

日志配置，控制 Xray 输出日志的方式.

> api:[ApiObject](./api.md)

提供了一些 API 接口供远程调用。

> dns: [DnsObject](./dns.md)

内置的 DNS 服务器. 如果没有配置此项，则使用系统的 DNS 设置。

> routing: [RoutingObject](./routing.md)

路由功能。可以设置规则分流数据从不同的 outbound 发出.

> policy: [PolicyObject](./policy.md)

本地策略，可以设置不同的用户等级和对应的策略设置。

> inbounds: \[ [InboundObject](./inbound.md) ]

一个数组，每个元素是一个入站连接配置。

> outbounds: \[ [OutboundObject](./outbound.md) ]

一个数组，每个元素是一个出站连接配置。

> transport: [TransportObject](./transport.md)

用于配置 Xray 其它服务器建立和使用网络连接的方式。

> stats: [StatsObject](./stats.md)

用于配置流量数据的统计。

> fakedns: [FakeDnsObject](./fakedns.md)

FakeDNS 配置。可配合透明代理使用，以获取实际域名。

> metrics: [metricsObject](./metrics.md)

metrics 配置。更直接（希望更好）的统计导出方式。

> observatory: [ObservatoryObject](./observatory.md#observatoryobject)

后台连接观测。探测出站代理的连接状态。

> burstObservatory: [BurstObservatoryObject](./observatory.md#burstobservatoryobject)

突发连接观测。探测出站代理的连接状态。

> geodata: [GeodataObject](./geodata.md)

地理数据文件自动更新与热重载。

---

---
url: /config/log.md
---
# 日志配置

日志配置，控制 Xray 输出日志的方式.

Xray 有两种日志, 访问日志和错误日志, 你可以分别配置两种日志的输出方式.

## LogObject

LogObject 对应配置文件的 `log` 项。

```json
{
  "log": {
    "access": "文件地址",
    "error": "文件地址",
    "loglevel": "warning",
    "dnsLog": false,
    "maskAddress": ""
  }
}
```

> `access`: string

访问日志的文件地址，其值是一个合法的文件地址，如`"/var/log/Xray/access.log"`（Linux）或者`"C:\\Temp\\Xray\\_access.log"`（Windows）。当此项不指定或为空值时，表示将日志输出至 stdout。

* 特殊值`none`，即关闭 access log。

> `error`: string

错误日志的文件地址，其值是一个合法的文件地址，如`"/var/log/Xray/error.log"`（Linux）或者`"C:\\Temp\\Xray\\_error.log"`（Windows）。当此项不指定或为空值时，表示将日志输出至 stdout。

* 特殊值`none`，即关闭 error log。

> `loglevel`: "debug" | "info" | "warning" | "error" | "none"

error 日志的级别, 指示 error 日志需要记录的信息.
默认值为 `"warning"`。

* `"debug"`：调试程序时用到的输出信息。同时包含所有 `"info"` 内容。
* `"info"`：运行时的状态信息等，不影响正常使用。同时包含所有 `"warning"` 内容。
* `"warning"`：发生了一些并不影响正常运行的问题时输出的信息，但有可能影响用户的体验。同时包含所有 `"error"` 内容。
* `"error"`：Xray 遇到了无法正常运行的问题，需要立即解决。
* `"none"`：不记录任何内容。

> `dnsLog`: bool

是否启用 DNS 查询日志，例如：`DOH//doh.server got answer: domain.com -> [ip1, ip2] 2.333ms`

> `maskAddress`: "quarter" | "half" | "full"

IP地址遮罩，启用后将自动替换log中出现的IP地址，用于在分享日志时保护隐私，默认为空即不启用。

目前可选等级 `quarter` `half` `full` 遮罩形式对应如下

* ipv4 `1.2.*.*` `1.*.*.*` `[Masked IPv4]`
* ipv6 `1234:5678::/32` `1234::/16` `[Masked IPv6]`

有更详细的需求可以使用自定义格式 如 `/16+/32` 自定义需要遮罩到位数，前为 IPv4 后为 IPv6，其中 IPv4 必须可以被 8 整除。/32 或者 /128 表示完全不遮罩， /0 表示显示为 `[Masked IPv4/IPv6]`

---

---
url: /config/api.md
---
# API 接口

API 接口配置提供了一些基于 [gRPC](https://grpc.io/)的 API 接口供远程调用。

可以通过 api 配置模块开启接口。当 api 配置开启时，Xray 会自建一个和 tag 同名的出站代理，须手动将所有的 API 入站连接通过 [路由规则配置](./routing.md) 指向这一出站代理。请参考本节中的 [相关配置](#相关配置)。

[v1.8.12](https://github.com/XTLS/Xray-core/releases/tag/v1.8.12) 起支持简易配置模式，只配置 ApiObject 即可，不需要配置 inbounds 和 routing。但是使用简易配置时，流量统计功能不统计 API 入站连接的流量。

::: warning
大多数用户并不会用到此 API，新手可以直接忽略这一项。
:::

## ApiObject

`ApiObject` 对应配置文件的 `api` 项。

```json
{
  "api": {
    "tag": "api",
    "listen": "127.0.0.1:8080",
    "services": [
      "HandlerService",
      "LoggerService",
      "StatsService",
      "RoutingService"
    ]
  }
}
```

> `tag`: string

出站代理标识。

> `listen`: string

API 服务监听的 IP 和端口。这是一个可选配置项。

省略这项时需要按照下面[相关配置](#相关配置)中的示例，添加 inbounds 和 routing 配置。

> `services`: \[string]

开启的 API 列表，可选的值见 [API 列表](#支持的-api-列表)。

## 相关配置

可以在 inbounds 配置中增加一个 api 的 inbound

```json
"inbounds": [
  {
    "listen": "127.0.0.1",
    "port": 10085,
    "protocol": "dokodemo-door",
    "settings": {
      "rewriteAddress": "127.0.0.1"
    },
    "tag": "api"
  }
]
```

在路由配置中增加针对 api inbound 的路由规则

```json
"routing": {
  "rules": [
    {
      "inboundTag": [
        "api"
      ],
      "outboundTag": "api"
    }
  ]
}
```

在基础配置中增加 api

```json
"api": {
  "tag": "api",
  "services": [
    "StatsService"
  ]
}
```

## 支持的 API 列表

### HandlerService

一些对于入站出站代理进行修改的 API，可用的功能如下：

* 添加一个新的入站代理；
* 添加一个新的出站代理；
* 删除一个现有的入站代理；
* 删除一个现有的出站代理；
* 列出出站代理；
* 列出入站代理
* 在一个入站代理中添加一个用户（仅支持 VMess、VLESS、Trojan、Shadowsocks）；
* 在一个入站代理中删除一个用户（仅支持 VMess、VLESS、Trojan、Shadowsocks）；

### RoutingService

添加、删除、替换 routing 规则，查询均衡器统计信息的 API，可用的功能如下：

* adrules 添加、替换 routing 配置
* rmrules 删除 routing 规则
* sib 断开来源 IP 的连接
* bi 查询均衡器统计信息
* bo 强制均衡器选中指定的 outboundTag

可以使用类似于 `./xray help api bi` 这样的命令来查询具体用法。

### LoggerService

支持对内置 Logger 的重启，可配合 logrotate 进行一些对日志文件的操作。

### StatsService

内置的数据统计服务，详见 [统计信息](./stats.md)。

### ReflectionService

支持 gRPC 客户端获取服务端的 API 列表。

```bash
$ grpcurl -plaintext localhost:10085 list
grpc.reflection.v1alpha.ServerReflection
v2ray.core.app.proxyman.command.HandlerService
v2ray.core.app.stats.command.StatsService
xray.app.proxyman.command.HandlerService
xray.app.stats.command.StatsService
```

## API 调用示例

[Xray-API-documents](https://github.com/XTLS/Xray-API-documents) @crossfw

---

---
url: /config/dns.md
---
# 内置 DNS 服务器

## DNS 服务器

Xray 内置的 DNS 模块，主要有三大用途：

* 在路由阶段，解析域名为 IP, 并且根据域名解析得到的 IP 进行规则匹配以分流。是否解析域名及分流和路由配置模块中 `domainStrategy` 的值有关，只有在设置以下两种值时，才会使用内置 DNS 服务器进行 DNS 查询：
  * "IPIfNonMatch", 请求一个域名时，进行路由里面的 domain 进行匹配，若无法匹配到结果，则对这个域名使用内置 DNS 服务器进行 DNS 查询，并且使用查询返回的 IP 地址再重新进行 IP 路由匹配。
  * "IPOnDemand", 当匹配时碰到任何基于 IP 的规则，将域名立即解析为 IP 进行匹配。

* 解析目标地址进行连接：
  * 如 在 `freedom` 出站中，将 `domainStrategy` 设置为 `UseIP`, 由此出站发出的请求, 会先将域名通过内置服务器解析成 IP, 然后进行连接。
  * 如 在 `sockopt` 中，将 `domainStrategy` 设置为 `UseIP`, 此出站发起的系统连接，将先由内置服务器解析为 IP, 然后进行连接。

* TUN/透明代理时通过路由和 DNS 出站组合，以劫持 DNS 流量到此模块；或直接对外暴露 53 端口充当递归 DNS 服务器。

::: tip TIP 1
DNS 服务器默认进入路由系统进行匹配，除非其包含 `+local` 在其中使用域名时，注意可能的回环问题，`hosts` 可能有帮助。
:::

::: tip TIP 2
只支持最基本的 IP 查询（A 和 AAAA 记录），CNAME 记录将会重复查询直至返回 A/AAAA 记录为止。其它查询不会进入内置 DNS 服务器，而是根据你在出站中的配置可以丢弃或透传给其它服务器。
:::

## DNS 处理流程

域名将先执行 Hosts 映射检查（详见 `hosts` 字段），若没有查出需要的 IP，则继续使用 DNS 服务器进行查询。

而后核心将开始构建一个列表，核心会根据请求的域名，将服务器进行排序，遵循以下规则。

* 构建列表 1：包含 `domains` 字段成功命中了请求域名的服务器，顺序与配置文件中相同。
* 检查 `disableFallback` 若为真则跳过构建列表 2。
* 检查 `disableFallbackIfMatch` 若为真且列表 1 不为空则跳过构建列表 2。
* 构建列表 2：包含不在列表 1 且 `skipFallback` 不为真的服务器，顺序与配置文件中相同。
* 最终服务器列表 = 列表 1 + 列表 2。

注：任何 FinalQuery 为真的DNS服务器都将直接截断后面的部分。

执行 DNS 查询时，核心将依次使用最终服务器列表中的服务器进行查询，并使用 `expectedIPs` 和 `unexpectedIPs` 过滤结果，为空则尝试列表中的下一个。（`enableParallelQuery` 为真时行为略有不同，详见其字段描述）

## DnsObject

`DnsObject` 对应配置文件的 `dns` 项。

```json
{
  "dns": {
    "hosts": {
      "baidu.com": "127.0.0.1",
      "dns.google": ["8.8.8.8", "8.8.4.4"]
    },
    "servers": [
      "8.8.8.8",
      "8.8.4.4",
      {
        "address": "1.2.3.4",
        "port": 5353,
        "domains": ["domain:xray.com"],
        "expectedIPs": ["geoip:cn"],
        "skipFallback": false,
        "clientIP": "1.2.3.4"
      },
      {
        "address": "https://8.8.8.8/dns-query",
        "domains": ["geosite:netflix"],
        "skipFallback": true,
        "queryStrategy": "UseIPv4"
      },
      {
        "address": "https://1.1.1.1/dns-query",
        "domains": ["geosite:openai"],
        "skipFallback": true,
        "queryStrategy": "UseIPv6"
      },
      "localhost"
    ],
    "clientIp": "1.2.3.4",
    "queryStrategy": "UseIP",
    "disableCache": false,
    "serveStale": false,
    "serveExpiredTTL": 0,
    "disableFallback": false,
    "disableFallbackIfMatch": false,
    "enableParallelQuery": false,
    "useSystemHosts": false,
    "tag": "dns_inbound"
  }
}
```

> `hosts`: map{string: address} | map{string: \[address]}

静态 IP 列表，其值为一系列的 "域名": "地址" 或 "域名": \["地址 1","地址 2"]。其中地址可以是 IP 或者域名。在解析域名时，核心将检查全部映射条目并返回全部匹配成功的 IP 地址。如果未命中，则进入 DNS 查询阶段。

映射的目标可以是域名，当核心完成匹配，并且其中包含域名时行为会略有不同：

* 其中同时包含 IP 和域名时，删去其中的域名，只返回 IP 地址。
* 其中包含数个域名，存在歧义，查询失败，视为未命中，进入 DNS 查询阶段。
* 其中有且仅有一个域名时，这个域名将重新进入 Hosts 模块递归解析，重复上面的步骤，最大递归深度为 5。
* 上一条递归查询未查询到 IP，并且最终查询结果有且仅有一个域名，该域名替代原始请求域名，进入 DNS 查询阶段。

其匹配格式（`domain:` `full:` 等等）同常用的 [路由系统](./routing.md#ruleobject) 中的 domain. 不同的是无前缀时此处默认使用 `full:` 前缀（类似常见的 hosts 文件写法）

> `servers`: \[string | [DnsServerObject](#dnsserverobject) ]

一个 DNS 服务器列表，支持的类型有两种：DNS 地址（字符串形式）和 [DnsServerObject](#dnsserverobject) 。

当值为 `"localhost"` 时，表示使用本机预设的 DNS 配置。

当它的值是一个 DNS `"IP:Port"` 地址时，如 `"8.8.8.8:53"`，Xray 会使用此地址的指定 UDP 端口进行 DNS 查询。该查询遵循路由规则。不指定端口时，默认使用 53 端口。

当值是 `"tcp://host:port"` 的形式，如 `"tcp://8.8.8.8:53"`，Xray 会使用 `DNS over TCP` 进行查询。该查询遵循路由规则。不指定端口时，默认使用 53 端口。

当值是 `"tcp+local://host:port"` 的形式，如 `"tcp+local://8.8.8.8:53"`，Xray 会使用 `TCP 本地模式 (TCPL)` 进行查询。即 DNS 请求不会经过路由组件，直接通过 Freedom outbound 对外请求，以降低耗时。不指定端口时，默认使用 53 端口。

当值是 `"https://host:port/dns-query"` 的形式，如 `"https://dns.google/dns-query"`，Xray 会使用 `DNS over HTTPS` (RFC8484, 简称 DOH) 进行查询。有些服务商拥有 IP 别名的证书，可以直接写 IP 形式，比如 `https://1.1.1.1/dns-query`。也可使用非标准端口和路径，如 `"https://a.b.c.d:8443/my-dns-query"`

当值是 `"h2c://host:port/dns-query"` 的形式，如 `"h2c://dns.google/dns-query"`，Xray 会使用 `DNS over HTTPS` 的请求格式但是将会以明文 h2c 发出请求，不能直接使用，在这种情况下需要自行配置 Freedom 出站 + streamSettings 设置 TLS 为其配置 TLS 以包装成正常的 DOH 请求。用于特殊目的，比如想要自定义 DOH 请求的 SNI 或者使用 utls 的指纹时使用。

当值是 `"https+local://host:port/dns-query"` 的形式，如 `"https+local://dns.google/dns-query"`，Xray 会使用 `DOH 本地模式 (DOHL)` 进行查询，即 DOH 请求不会经过路由组件，直接通过 Freedom outbound 对外请求，以降低耗时。一般适合在服务端使用。也可使用非标端口和路径。

当值是 `"quic+local://host"` 的形式，如 `"quic+local://dns.adguard.com"`，Xray 会使用 `DNS over QUIC 本地模式 (DOQL)` 进行查询，即 DNS 请求不会经过路由组件，直接通过 Freedom outbound 对外请求。该方式需要 DNS 服务器支持 DNS over QUIC。默认使用 853 端口进行查询，可以使用非标端口。

当值是 `fakedns` 时，将使用 FakeDNS 功能进行查询。

::: tip TIP 1
当使用 `localhost` 时，本机的 DNS 请求不受 Xray 控制，需要额外的配置才可以使 DNS 请求由 Xray 转发。
:::

::: tip TIP 2
不同规则初始化得到的 DNS 客户端会在 Xray 启动日志中以 `info` 级别体现，比如 `local DOH`、`remote DOH` 和 `udp` 等模式。
:::

::: tip TIP 3
(v1.4.0+) 可以在 [日志](./log.md) 中打开 DNS 查询日志。
:::

> `clientIp`: string

EDNS Client Subnet 扩展中使用的 IP 地址。

需要是一个有效的 IPv4 或者 IPv6. 实际发送时会自动抹掉最后几位，IPv4 和 IPv6 分别以 /24 和 /96 的子网发送。

> `queryStrategy`: "UseIP" | "UseIPv4" | "UseIPv6" | "UseSystem"

限制 DNS 模块中所有服务器的能力，以及由 Xray 自身发起的 IP 查询类型的默认值。

默认值 `UseIP` 允许查询 A + AAAA。由 Xray 自身发起的查询未指定 IP 类型时，同时向上游 DNS 服务器查询 A 和 AAAA 记录。`UseIPv4` 只查询且只允许查询 A 记录；`UseIPv6` 只查询且只允许查询 AAAA 记录。

`UseSystem` 自适应操作系统网络环境。查询前分别检查是否有 IPv4 和 IPv6 的默认网关，以此限制所有服务器的能力并设置查询类型的默认值。在图形环境操作系统上实时检查，在命令行环境只检查一次。

```json
    "dns": {
        "servers": [
            "https://1.1.1.1/dns-query",
            {
                "address": "https://8.8.8.8/dns-query",
                "domains": [
                    "geosite:netflix"
                ],
                "skipFallback": true,
                "queryStrategy": "UseIPv4" // netflix 的域名查询 A 记录
            },
            {
                "address": "https://1.1.1.1/dns-query",
                "domains": [
                    "geosite:openai"
                ],
                "skipFallback": true,
                "queryStrategy": "UseIPv6" // openai 的域名查询 AAAA 记录
            }
        ],
        "queryStrategy": "UseIP" // 全局同时查询 A 和 AAAA 记录
    }
```

::: tip TIP 1
全局 `"queryStrategy"` 值优先，当子项中的 `"queryStrategy"` 值与全局 `"queryStrategy"` 值冲突时，子项的查询将空响应。
:::

::: tip TIP 2
当子项中不写 `"queryStrategy"` 参数时，使用全局 `"queryStrategy"` 参数值。与 Xray-core v1.8.6 以前版本行为相同。
:::

例如：
全局 `"queryStrategy": "UseIPv6"` 与 子项 `"queryStrategy": "UseIPv4"` 冲突。
全局 `"queryStrategy": "UseIPv4"` 与 子项 `"queryStrategy": "UseIPv6"` 冲突。
全局 `"queryStrategy": "UseIP"` 与 子项 `"queryStrategy": "UseIPv6"` 不冲突。
全局 `"queryStrategy": "UseIP"` 与 子项 `"queryStrategy": "UseIPv4"` 不冲突。

```json
    "dns": {
        "servers": [
            "https://1.1.1.1/dns-query",
            {
                "address": "https://8.8.8.8/dns-query",
                "domains": [
                    "geosite:netflix"
                ],
                "skipFallback": true,
                "queryStrategy": "UseIPv6" // 全局 "UseIPv4" 与 子项 "UseIPv6" 冲突
            }
        ],
        "queryStrategy": "UseIPv4"
    }
```

子项 netflix 的域名查询由于 `"queryStrategy"` 值冲突，得到空响应。netflix 的域名由 `https://1.1.1.1/dns-query` 查询，得到 A 记录。

> `disableCache`: true | false

`true` 禁用 DNS 缓存，默认为 `false`，即不禁用。

它不会对 `localhost` DNS (系统 DNS) 生效，它总是跟随 golang 的 DNS 缓存行为(cgo 与 pure go 可能略有不同)。

> `serveStale`: true | false

`true` 启用 DNS 乐观缓存，默认为 `false`，即不启用。

仅服务器启用 DNS 缓存时才有效，也就是此选项受 `disableCache` 的约束。

> `serveExpiredTTL`: number

乐观缓存有效期，单位是秒，默认为 0 即永不过期。

若服务器已启用了缓存，并开启了乐观缓存。当缓存已过期，但乐观缓存未过期时，立即返回缓存中陈旧的 DNS 记录，并后台刷新缓存。这样可以降低延迟。

> `disableFallback`: true | false

`true` 禁用 DNS 的 fallback 查询，默认为 `false`，即不禁用。

> `disableFallbackIfMatch`: true | false

`true` 当 DNS 服务器的优先匹配域名列表命中时，禁用 fallback 查询，默认为 `false`，即不禁用。

> `enableParallelQuery`: true | false

`true` 启用并行查询，默认为 `false`，即不启用。

DNS 回退（failover）默认是串行的，即默认仅在选中的 DNS 服务器查询失败或 `expectedIPs` 和 `unexpectedIPs` 不匹配后才向下一台服务器发起查询。

启用并行查询后，会预先向所有被选中的 DNS 服务器异步发起查询，并执行“动态分组，组内竞速，组间回退”的策略。

动态分组，被选中的服务器列表中**相邻**的服务器如果 `clientIP` `skipFallback` `queryStrategy` `tag` `domains` `expectedIPs` `unexpectedIPs` **完全**一样，被视为同一个组。

组内竞速，同组内只要任意一 DNS 服务器查询成功且 `expectedIPs` 和 `unexpectedIPs` 匹配到了 IP，则本组视为成功，同时忽略本组其它服务器的结果。

组间回退，若第一个组还在查询，则等待。若第一个组成功，则返回 IP。若第一个组所有服务器都查询失败或 IP 不匹配，则回退到下一个组。最终若所有组都失败，则返回空解析。

> `useSystemHosts`: true | false

如果为真，将系统 hosts 文件附加到内置 DNS 的 hosts 中。

> `tag`: string

由内置 DNS 发出的查询流量，除 `localhost`、`fakedns`、`TCPL`、`DOHL` 和 `DOQL` 模式外，都可以用此标识在路由使用 `inboundTag` 进行匹配。

### DnsServerObject

```json
{
  "address": "1.2.3.4",
  "port": 5353,
  "domains": ["domain:xray.com"],
  "expectedIPs": ["geoip:cn"],
  "unexpectedIPs": ["geoip:cloudflare"],
  "skipFallback": false,
  "finalQuery": false,
  "tag": "dns-tag",
  "clientIP": "1.2.3.4",
  "queryStrategy": "UseIPv4",
  "disableCache": false
}
```

> `address`: address

一个 DNS 服务器列表，支持的类型有两种：DNS 地址（字符串形式）和 DnsServerObject 。

当值为 `"localhost"` 时，表示使用本机预设的 DNS 配置。

当它的值是一个 DNS `"IP"` 地址时，如 `"8.8.8.8"`，Xray 会使用此地址的指定 UDP 端口进行 DNS 查询。该查询遵循路由规则。默认使用 53 端口。

当值是 `"tcp://host"` 的形式，如 `"tcp://8.8.8.8"`，Xray 会使用 `DNS over TCP` 进行查询。该查询遵循路由规则。默认使用 53 端口。

当值是 `"tcp+local://host"` 的形式，如 `"tcp+local://8.8.8.8"`，Xray 会使用 `TCP 本地模式 (TCPL)` 进行查询。即 DNS 请求不会经过路由组件，直接通过 Freedom outbound 对外请求，以降低耗时。不指定端口时，默认使用 53 端口。

当值是 `"https://host:port/dns-query"` 的形式，如 `"https://dns.google/dns-query"`，Xray 会使用 `DNS over HTTPS` (RFC8484, 简称 DOH) 进行查询。有些服务商拥有 IP 别名的证书，可以直接写 IP 形式，比如 `https://1.1.1.1/dns-query`。也可使用非标准端口和路径，如 `"https://a.b.c.d:8443/my-dns-query"`

当值是 `"https+local://host:port/dns-query"` 的形式，如 `"https+local://dns.google/dns-query"`，Xray 会使用 `DOH 本地模式 (DOHL)` 进行查询，即 DOH 请求不会经过路由组件，直接通过 Freedom outbound 对外请求，以降低耗时。一般适合在服务端使用。也可使用非标端口和路径。

当值是 `"quic+local://host:port"` 的形式，如 `"quic+local://dns.adguard.com"`，Xray 会使用 `DOQ 本地模式 (DOQL)` 进行查询，即 DNS 请求不会经过路由组件，直接通过 Freedom outbound 对外请求。该方式需要 DNS 服务器支持 DNS over QUIC。默认使用 853 端口进行查询，可以使用非标端口。

当值是 `fakedns` 时，将使用 FakeDNS 功能进行查询。

::: tip 关于 local 模式和 DNS 服务器本身的域名
由 DNS 模块发出的 DNS 请求有两种情况：

local 模式将直接由核心向外连接，这种情况下如果地址是一个域名将交由系统本身进行解析，逻辑较为简单。

非 local 默认将视为一个从 tag 为 dns.tag(不知道在哪？ 浏览器 ctrl+f 搜索 `inboundTag`) 的入站进来的请求，将经过正常的核心处理流程，可能会被路由模块分配去本地 freedom 或者其他远端出站，它将被 freedom 的 domainStrategy 解析(注意可能的回环) 或者直接以域名的形式被传送到远端根据服务端本身的解析方式解析。

由于普通人可能难以理清其中的逻辑，建议(特别是在透明代理的环境下)，直接在 DNS 模块的 host 选项中直接为带域名的服务器设置它们对应的 IP 防止出现回环。

顺便 DNS 模块非 local 模式发出的 DNS 请求将会自动在路由模块中跳过 IPIfNonMatch 和 IPOnDemand 的解析过程，防止它们的解析会被送回 DNS 模块导致回环。
:::

> `port`: number

DNS 服务器端口，如 `53`。此项缺省时默认为 `53`。当使用 DOH、DOHL、DOQL 模式时该项无效，非标端口应在 URL 中指定。

> `domains`: \[string]

一个域名列表，此列表包含的域名，将优先使用此服务器进行查询。域名格式和 [路由配置](./routing.md#ruleobject) 中相同。

> `expectedIPs`:\[string]

一个 IP 范围列表，格式和 [路由配置](./routing.md#ruleobject) 中相同。

当配置此项时，Xray DNS 会对返回的 IP 的进行校验，只返回包含 expectedIPs 列表中的地址。

如果列表中存在 \* 那么如果过滤后不存在 IP, 仍然返回原 IP 使请求不至于失败。

> `unexpectedIPs`: \[string]

`expectedIPs` 的反向版本，去掉包含于这个列表的 IP. 星号的作用相同。

> `skipFallback`: true | false

`true`，在进行 DNS fallback 查询时将跳过此服务器, 默认为 `false`，即不跳过。

> `timeoutMs`: number

DNS 服务器超时时间，默认 4000 ms.

它不会对 `localhost` DNS (系统 DNS) 生效，它总是跟随 golang 的 DNS 超时行为(cgo 与 pure go 可能略有不同)。

> `finalQuery`: true | false

如果设置为真，该 DNS 服务器的请求会是最终尝试，不会触发 fallback 行为。

> `queryStrategy`: "UseIP" | "UseIPv4" | "UseIPv6" | "UseSystem"

若未指定，将继承自全局配置；若指定，则允许更进一步限制此服务器能力，以及设置由 Xray 自身发起的 IP 查询类型的默认值。

注意：它始终受全局 `queryStrategy` 的约束。

### 以下配置项若未指定，将继承自全局配置，也可以在这里覆盖全局配置

> `tag`: string

> `clientIP`: \[string]

> `disableCache`: true | false

> `serveStale`: true | false

> `serveExpiredTTL`: number

---

---
url: /config/fakedns.md
---
# FakeDNS

FakeDNS 通过伪造 DNS 以获取目标域名，能够降低 DNS 查询时的延迟、配合透明代理获取目标域名。

::: warning
FakeDNS 有可能会污染本地 DNS，导致 Xray 关闭后“无法访问网络”。
:::

## FakeDNSObject

`FakeDNSObject` 对应配置文件的 `fakedns` 项。

```json
{
  "fakedns": {
    "ipPool": "198.18.0.0/16",
    "poolSize": 65535
  }
}
```

`FakeDnsObject` 亦可配置为一个包含多个 FakeIP Pool 的数组。当收到 DNS 查询请求时，FakeDNS 会返回一组同时由多个 FakeIP Pool 得到的一组 FakeIP。

```json
{
  "fakedns": [
    {
      "ipPool": "198.18.0.0/15",
      "poolSize": 65535
    },
    {
      "ipPool": "fc00::/18",
      "poolSize": 65535
    }
  ]
}
```

> `ipPool`: CIDR

FakeDNS 将使用此选项指定的 IP 块分配地址。

> `poolSize`: int

指定 FakeDNS 储存的 域名-IP 映射的最大数目。当映射数超过此值后，会按照 LRU 规则淘汰映射。默认为 65535。

::: warning
`poolSize` 必须小于或等于 `ipPool` 对应的地址总数。
:::

::: tip
若配置文件中 `dns` 项设置了 `fakedns` 但配置文件没有设置 `FakeDnsObject`，Xray 会根据 DNS 组件的 `queryStrategy` 来初始化 `FakeDnsObject`。

`queryStrategy` 为 `UseIP` 时，初始化的 FakeIP Pool 相当于

```json
{
  "fakedns": [
    {
      "ipPool": "198.18.0.0/15",
      "poolSize": 32768
    },
    {
      "ipPool": "fc00::/18",
      "poolSize": 32768
    }
  ]
}
```

`queryStrategy` 为 `UseIPv4` 时，初始化的 FakeIP Pool 相当于

```json
{
  "fakedns": {
    "ipPool": "198.18.0.0/15",
    "poolSize": 65535
  }
}
```

`queryStrategy` 为 `UseIPv6` 时，初始化的 FakeIP Pool 相当于

```json
{
  "fakedns": {
    "ipPool": "fc00::/18",
    "poolSize": 65535
  }
}
```

:::

### 如何使用？

FakeDNS 本质上是一个 [DNS 服务器](./dns.md#serverobject)，能够与任意 DNS 规则配合使用。

只有将 DNS 查询路由到 FakeDNS，才能使其发挥作用。

```json
{
  "dns": {
    "servers": [
      "fakedns", // fakedns 排在首位
      "8.8.8.8"
    ]
  },
  "outbounds": [
    {
      "protocol": "dns",
      "tag": "dns-out"
    }
  ],
  "routing": {
    "rules": [
      {
        "inboundTag": ["xxx"], // 劫持来自 DNS 查询入口的 DNS 流量，或劫持来自透明代理入站的 DNS 流量。
        "port": 53,
        "outboundTag": "dns-out"
      }
    ]
  }
}
```

当外部 DNS 请求进入 FakeDNS 组件时，它会返回位于自己 `ipPool` 内的 IP 地址作为域名的虚构解析结果，并记录该域名与虚构解析结果之间的映射关系。

另外，你需要在**客户端**接收需代理流量的入站中开启 `Sniffing`，并使用 `fakedns` 目标地址重置。

```json
{
  "inbounds": [
    {
      // ...
      // [!code focus:5]
      "sniffing": {
        "enabled": true,
        "destOverride": ["fakedns"], // 使用 "fakedns"，或与其它 sniffer 搭配使用
        "metadataOnly": false // 此项为 true 时 destOverride 仅可使用 fakedns
      }
    }
  ]
}
```

::: warning
如果 FakeIP 没有被正确的还原为域名，将无法连接到服务器。
:::

### 与其它类型 DNS 搭配使用

#### 与 DNS 分流共存

使用 DNS 分流时，为了使 `fakedns` 拥有高优先级，需要对其增加与其他类型 DNS 相同的 `domains`。

```json
{
  "dns": {
    "servers": [
      // [!code focus:13]
      {
        "address": "fakedns",
        "domains": [
          // 与下方分流所用的内容一致
          "geosite:cn",
          "domain:example.com"
        ]
      },
      {
        "address": "1.2.3.4",
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      {
        "address": "1.1.1.1",
        "domains": ["domain:example.com"]
      },
      "8.8.8.8"
    ]
  }
}
```

#### FakeDNS 黑名单

如不希望某些域名使用 FakeDNS，则可在其它类型的 DNS 配置中添加 `domains` 配置，使指定域名在匹配时其它 DNS 服务器拥有比 FakeDNS 更高的优先级，进而实现 FakeDNS 的黑名单机制。

```json
{
  "dns": {
    "servers": [
      "fakedns",
      {
        "address": "1.2.3.4",
        "domains": ["domain:do-not-use-fakedns.com"]
      }
    ]
  }
}
```

#### FakeDNS 白名单

如希望仅某些域名使用 FakeDNS，则可在 `fakedns` 增加 `domains` 配置，使指定域名在匹配时 `fakedns` 拥有比其它 DNS 服务器更高的优先级，进而实现 FakeDNS 的白名单机制。

```json
{
  "dns": {
    "servers": [
      "1.2.3.4",
      {
        "address": "fakedns",
        "domains": ["domain:only-this-use-fakedns.com"]
      }
    ]
  }
}
```

---

---
url: /config/inbound.md
---
# 入站代理

入站连接用于接收发来的数据，可用的协议请见[入站协议](./inbounds/)。

## InboundObject

`InboundObject` 对应配置文件中 `inbounds` 项的一个子元素。

```json
{
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1080,
      "protocol": "协议名称",
      "settings": {},
      "streamSettings": {},
      "tag": "标识",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ]
}
```

> `listen`: address

监听地址，IP 地址或 Unix domain socket，默认值为 `"0.0.0.0"`，表示接收所有网卡上的连接.

可以指定一个系统可用的 IP 地址。

`"::"` 等价于`"0.0.0.0"` 两者都会同时监听 IPv6 和 IPv4. 不过如果只想监听IPv6可以将 `sockopt` 的 `v6only` 设置为 true. 如果只想监听ipv4 可以用 `ip a` 等命令查看网卡上的具体 IP (通常直接就是机器的公网 IP 地址或者一个类似 10.x.x.x 的内网地址) 然后监听，当然对于 IPv6 也可以这么做。

注意，因为UDP不是面向连接的，如果入站基于 UDP 且网卡上存在多个IP地址而外部连接的是网卡上的非首选地址，将会导致 Xray 错误地使用首选地址而非外部连接的目标作为源地址回复导致连接不通。
解决办法是不要监听 `0.0.0.0` 而是监听网卡上具体的 IP 地址。

支持填写 Unix domain socket，格式为绝对路径，形如 `"/dev/shm/domain.socket"`，可在开头加 `@` 代表 [abstract](https://www.man7.org/linux/man-pages/man7/unix.7.html)，`@@` 则代表带 padding 的 abstract。

填写 Unix domain socket 时，`port` 和 `allocate` 将被忽略，协议目前可选 VLESS、VMess、Trojan，仅适用于基于 TCP 的底层传输，如 `tcp` `websocket` `grpc`. 不支持基于 UDP 的传输，如 `mkcp`.

填写 Unix domain socket 时，填写为形如 `"/dev/shm/domain.socket,0666"` 的形式，即 socket 后加逗号及访问权限指示符，即可指定 socket 的访问权限，可用于解决默认情况下出现的 socket 访问权限问题。

> `port`: number | "env:variable" | string

端口。接受的格式如下:

* 整型数值：实际的端口号。
* 环境变量：以 `"env:"` 开头，后面是一个环境变量的名称，如 `"env:PORT"`。Xray 会以字符串形式解析这个环境变量。
* 字符串：可以是一个数值类型的字符串，如 `"1234"`；或者一个数值范围，如 `"5-10"` 表示端口 5 到端口 10，这 6 个端口。可以使用逗号进行分段，如 `11,13,15-17` 表示端口 11、端口 13、端口 15 到端口 17 这 5 个端口。

当只有一个端口时，Xray 会在此端口监听入站连接。当指定了一个端口范围时，范围内的端口都会由 Xray 监听。

注意，监听一个端口是相当昂贵的操作，监听端口范围太大可能造成占用显著提高甚至导致 Xray 无法正常工作，一般来说监听数量接近四位数时可能就会开始出现问题，要使用一个很大的范围请考虑使用 iptables 进行重定向而不是在这里设置。

> `protocol`: "dokodemo-door" | "http" | "shadowsocks" | "socks" | "vless" | "vmess" | "trojan" | "wireguard" | "hysteria"

连接协议名称，可选的协议列表见左侧 [入站协议](./inbounds/)。

> `settings`: InboundConfigurationObject

具体的配置内容，视协议不同而不同。详见每个协议中的 `InboundConfigurationObject`。

> `streamSettings`: [StreamSettingsObject](./transport.md#streamsettingsobject)

底层传输方式（transport）是当前 Xray 节点和其它节点对接的方式

> `tag`: string

此入站连接的标识，用于在其它的配置中定位此连接。

::: danger
当其不为空时，其值必须在所有 `tag` 中**唯一**。
:::

> `sniffing`: [SniffingObject](#sniffingobject)

流量探测主要作用于在透明代理等用途，比如一个典型流程如下：

1. 如有一个设备上网，去访问 abc.com，首先设备通过 DNS 查询得到 abc.com 的 IP 是 1.2.3.4, 然后设备会向 1.2.3.4 去发起连接。
2. 如果不设置嗅探，Xray 收到的连接请求是 1.2.3.4, 并不能用于域名规则的路由分流。
3. 当设置了 sniffing 中的 enable 为 true，Xray 处理此连接的流量时，会从流量的数据中，嗅探出域名，即 abc.com。
4. Xray 会把 1.2.3.4 重置为 abc.com。路由就可以根据域名去进行路由的域名规则的分流。

因为变成了一个向 abc.com 请求的连接，就可以做更多的事情，除了路由域名规则分流，还能重新做 DNS 解析等其他工作。

当设置了 sniffing 中的 enable 为 true，还能嗅探出 bittorrent 类型的流量，然后可以在路由中配置 "protocol" 项来设置规则处理未加密的 BT 流量，比如服务端用来拦截未加密的 BT 流量，或客户端固定转发 BT 流量到某个 VPS 去等。

注意：较新的浏览器可能使用 ECH 加密 Client Hello, 此时 Xray 只能看到 Outer Hello 中的域名，可能需要考虑劫持 DNS 或者手动在浏览器配置关闭 ECH.

### SniffingObject

```json
{
  "enabled": true,
  "destOverride": ["http", "tls", "fakedns"],
  "metadataOnly": false,
  "domainsExcluded": [],
  "ipsExcluded": [],
  "routeOnly": false
}
```

> `enabled`: true | false

是否开启流量探测。

> `destOverride`: \["http" | "tls" | "quic" | "fakedns"]

当流量为指定类型时，按其中包括的目标地址重置当前连接的目标。

::: tip
Xray只会嗅探 `destOverride` 中协议的域名用作路由，如果只想进行嗅探用作路由而不想重置目标地址（如使用Tor浏览器时，重置目标地址会导致无法连接），请在这里添加对应的协议并启用 `routeOnly` 。
:::

> `metadataOnly`: true | false

当启用时，将仅使用连接的元数据嗅探目标地址。此时，除 `fakedns` 以外的 sniffer 将不能激活。

如果关闭仅使用元数据推断目标地址，此时客户端必须先发送数据，代理服务器才会实际建立连接。此行为与需要服务器首先发起第一个消息的协议不兼容，如 SMTP 协议。

> `domainsExcluded`: \[string]

一个域名列表，如果流量探测结果在这个列表中时，将 **不会** 重置目标地址。格式和 [路由配置](./routing.md#ruleobject) 中相同。

::: tip
填写一些域名，可能解决iOS 推送通知，米家智能设备，某些游戏（彩虹六号）语音问题。
如果需要排查某些问题的原因，可以通过关闭 `"sniffing"` 或者启用 `"routeOnly"` 来测试。
:::

```json
"domainsExcluded": [
    "courier.push.apple.com", // iOS 推送通知
    "Mijia Cloud", // 米家智能设备
    "dlg.io.mi.com"
]

```

> `ipsExcluded`: \[string]

一个 IP 列表，如果目标地址在这个列表中，将 **不会** 重置目标地址。格式和 [路由配置](./routing.md#ruleobject) 中相同。

> `routeOnly`: true | false

将嗅探得到的域名仅用于路由，代理目标地址仍为 IP。默认值为 `false`。

此项需要开启 `destOverride` 使用。

::: tip
在能保证 **被代理连接能得到正确的 DNS 解析** 时，使用 `routeOnly` 且开启 `destOverride` 的同时，将路由匹配策略 `domainStrategy` 设置为 `AsIs` 即可实现全程无 DNS 解析进行域名及 IP 分流。此时遇到 IP 规则匹配时使用的 IP 为域名原始 IP。
:::

---

---
url: /config/outbound.md
---
# 出站代理（Mux、XUDP）

出站连接用于发送数据，可用的协议请见 [出站协议](./outbounds/)。

## OutboundObject

`OutboundObject` 对应配置文件中 `outbounds` 项的一个子元素。

::: tip
列表中的第一个元素作为主 outbound。当路由匹配不存在或没有匹配成功时，流量由主 outbound 发出。
:::

```json
{
  "outbounds": [
    {
      "sendThrough": "0.0.0.0",
      "protocol": "协议名称",
      "settings": {},
      "tag": "标识",
      "streamSettings": {},
      "proxySettings": {
        "tag": "another-outbound-tag",
        "transportLayer": false
      },
      "mux": {},
      "targetStrategy": "AsIs"
    }
  ]
}
```

> `sendThrough`: address

用于发送数据的 IP 地址，当主机有多个 IP 地址时有效，默认值为 `"0.0.0.0"`。

允许在其中填入 IPv6 CIDR 块 (如 114:514:1919:810::/64)，Xray 将会使用地址块中随机的 IP 地址对外发起连接。
需要正确配置网络接入方式，路由表以及内核参数以允许 Xray bind 至地址块内的任意 IP。

对于使用 ndp 接入的网络，不建议设置小于 /120 的子网，否则可能会造成 NDP flood 导致路由器邻居缓存被占满等一系列问题。

特殊值 `origin` 若使用该值，将使用本机被连接的 IP 发出请求。

特殊值 `srcip` 若使用该值，将使用入站时的源 IP 发出请求。

例如机器上存在一整段 IPv4 `11.4.5.0/24` 且监听 0.0.0.0 (网卡上的全部 IPv4 与 IPv6)，若客户端通过 `11.4.5.14` 连接到本机，那么出站也会通过 `11.4.5.14` 发送对外请求；如果使用 `11.4.5.10` 连接到本机，那么出站就会通过 `11.4.5.10` 发送请求。 同样适用于机器上有整段/复数个 IPv6 的情况。

和入站介绍的一样，因为 UDP 的无连接特性 Xray 无从得知这个请求进入核心的原目标 IP (举个例子，在同一个 QUIC 连接里它甚至可能变动)，所以这个功能无法生效。

> `protocol`: "blackhole" | "dns" | "freedom" | "http" | "loopback" | "shadowsocks" | "socks" | "trojan" | "vless" | "vmess" | "hysteria" | "wireguard"

连接协议名称，可选的协议列表见左侧 [出站协议](./outbounds/)。

> `settings`: OutboundConfigurationObject

具体的配置内容，视协议不同而不同。详见每个协议中的 `OutboundConfigurationObject`。

> `tag`: string

此出站连接的标识，用于在其它的配置中定位此连接。

::: danger
当其不为空时，其值必须在所有 `tag` 中 **唯一**。
:::

> `streamSettings`: [StreamSettingsObject](./transport.md#streamsettingsobject)

底层传输方式（transport）是当前 Xray 节点和其它节点对接的方式。

> `proxySettings`: [ProxySettingsObject](#proxysettingsobject)

出站代理配置。

> `mux`: [MuxObject](#muxobject)

Mux 相关的具体配置。

> `targetStrategy`: "AsIs" | "UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4" | "ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"

如果此出站尝试发送一个域名请求，控制其是否被解析/如何解析为 IP 并发送。

默认值为 `AsIs` 即保持原样发送到远端服务器。所有参数含义均约等于 [sockopt](./transport.md#sockoptobject) 中的 `domainStrategy`。

::: tip
这里控制的是**被代理的请求**，如果出站代理服务器的地址是域名，并需要为这个域名本身选择解析策略，则应配置 [sockopt](./transport.md#sockoptobject) 中的 `domainStrategy`。
:::

### ProxySettingsObject

```json
{
  "tag": "another-outbound-tag",
  "transportLayer": false
}
```

> `tag`: string

当指定另一个 outbound 的标识时，此 outbound 发出的数据，将被转发至所指定的 outbound 发出。

::: danger
此选项与 [SockOpt.dialerProxy](./transport.md#sockoptobject) 冲突，根据需要任选其一即可。

默认情况下，这种转发方式**不经过**底层传输方式 (REALITY/XHTTP/gRPC...)，也就是此 outbound 的 `streamSettings` 将不起作用。
如果需要使用支持底层传输方式的转发，请改用 `SockOpt.dialerProxy` 或者将 `transportLayer` 设为 `true`。
:::

> `transportLayer`: true | false

`true` 将此设置转化为 `SockOpt.dialerProxy` 来支持底层传输方式的转发，默认为 `false` 即不转化。

### MuxObject

Mux 功能是在一条 TCP 连接上分发多个 TCP 连接的数据。实现细节详见 [Mux.Cool](../development/protocols/muxcool.md)。Mux 是为了减少 TCP 的握手延迟而设计，而非提高连接的吞吐量。使用 Mux 看视频、下载或者测速通常都有反效果。Mux 只需要在客户端启用，服务器端自动适配。Mux 的第二个用途是分发多个 UDP 连接，即 XUDP。

`MuxObject` 对应 `OutboundObject` 中的 `mux` 项。

```json
{
  "enabled": true,
  "concurrency": 8,
  "xudpConcurrency": 16,
  "xudpProxyUDP443": "reject"
}
```

> `enabled`: true | false

是否启用 Mux 转发请求，默认值 `false`。

> `concurrency`: number

最大并发连接数。最小值 `1`，最大值 `128`。省略或者填 `0` 时都等于 `8`, 大于 `128` 的值都将视为 128, 因为当一个连接达到最大复用次数 128 后其将不会再被分配任何新的子连接。

这个数值表示了一个 TCP 连接上最多承载的子连接数量。比如设置 `concurrency=8` 时，当客户端发出了 8 个 TCP 请求，Xray 只会发出一条实际的 TCP 连接，客户端的 8 个请求全部由这个 TCP 连接传输。

当全部 Mux 连接的最大子连接连接都已被占满，核心将发出新的连接承载子连接。如果短时间内并发大量子连接后续逐渐减少，核心内部调度器将倾向于将请求分配至 2 个连接交替使用并闲置其他连接等待它们的所有子连接自然结束后将其关闭以节约资源。如果总子连接数继续长时间低于 `concurrency` 则在其中一条达到最大复用次数后核心将回到单连接状态。

::: tip
填负数时，如 `-1`，不使用 Mux 模块承载 TCP 流量。
:::

> `xudpConcurrency`: number

使用新 XUDP 聚合隧道（也就是另一条 Mux 连接）代理 UDP 流量，填写最大并发子 UoT 数量。最小值 `1`，最大值 `1024`。
省略或者填 `0` 时，将与 TCP 流量走同一条路，也就是传统的行为。

::: tip
填负数时，如 `-1`，不使用 Mux 模块承载 UDP 流量。将使用代理协议原本的 UDP 传输方式。例如 `Shadowsocks` 会使用原生 UDP，`VLESS` 会使用 UoT。
:::

> `xudpProxyUDP443`: string

控制 Mux 对于被代理的 UDP/443（QUIC）流量的处理方式：

* 默认 `reject` 拒绝流量（一般浏览器会自动回落到 TCP HTTP2）
* `allow` 允许走 Mux 连接。
* 填 `skip` 时，不使用 Mux 模块承载 UDP 443 流量。将使用代理协议原本的 UDP 传输方式。例如 `Shadowsocks` 会使用原生 UDP，`VLESS` 会使用 UoT。

---

---
url: /config/policy.md
---
# 本地策略

本地策略，可以设置不同的用户等级和对应的策略设置，比如连接超时设置。Xray 处理的每一个连接都对应一个用户，按照用户的等级（level）应用不同的策略。

## PolicyObject

`PolicyObject` 对应配置文件的 `policy` 项。

```json
{
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5,
        "statsUserUplink": false,
        "statsUserDownlink": false,
        "statsUserOnline": false,
        "bufferSize": 4
      }
    },
    "system": {
      "statsInboundUplink": false,
      "statsInboundDownlink": false,
      "statsOutboundUplink": false,
      "statsOutboundDownlink": false
    }
  }
}
```

> `level`: map{string: [LevelPolicyObject](#levelpolicyobject)}

一组键值对，每个键是一个字符串形式的数字（JSON 的要求），比如 `"0"`、`"1"` 等，双引号不能省略，此数字对应用户等级。每一个值是一个 [LevelPolicyObject](#levelpolicyobject).

::: tip
每个入站出站代理现在都可以设置用户等级，Xray 会根据实际的用户等级应用不同的本地策略。
:::

> `system`: [SystemPolicyObject](#systempolicyobject)

Xray 系统级别的策略

### LevelPolicyObject

```json
{
  "handshake": 4,
  "connIdle": 300,
  "uplinkOnly": 2,
  "downlinkOnly": 5,
  "statsUserUplink": false,
  "statsUserDownlink": false,
  "bufferSize": 10240
}
```

> `handshake`: number

连接建立时的握手时间限制。单位为秒。默认值为 `4`。在入站代理处理一个新连接时，在握手阶段如果使用的时间超过这个时间，则中断该连接。

> `connIdle`: number

连接空闲的时间限制。单位为秒。默认值为 `300`。inbound/outbound 处理一个连接时，如果在 `connIdle` 时间内，没有任何数据被传输（包括上行和下行数据），则中断该连接。

> `uplinkOnly`: number

当连接下行线路关闭后的时间限制。单位为秒。默认值为 `2`。当服务器（如远端网站）关闭下行连接时，出站代理会在等待 `uplinkOnly` 时间后中断连接。

> `downlinkOnly`: number

当连接上行线路关闭后的时间限制。单位为秒。默认值为 `5`。当客户端（如浏览器）关闭上行连接时，入站代理会在等待 `downlinkOnly` 时间后中断连接。

::: tip
在 HTTP 浏览的场景中，可以将 `uplinkOnly` 和 `downlinkOnly` 设为 `0`，以提高连接关闭的效率。
:::

> `statsUserUplink`: true | false

当值为 `true` 时，开启当前等级的所有用户的上行流量统计。

> `statsUserDownlink`: true | false

当值为 `true` 时，开启当前等级的所有用户的下行流量统计。

> `statsUserOnline`: true | false

当值为 `true` 时，开启当前等级的所有用户的在线数量统计。(在线标准：20秒内有过连接活动)

> `bufferSize`: number

每个请求的内部缓存大小，单位为 KB。注意，多个请求可能被多路复用承载在同一条连接上，比如使用 mux.cool 或者 GRPC 时，也即是说即使共享一条底层连接，它们的缓存池也是独立的。

当内部缓存大于该值时，只有内部缓存被发出直至小于等于该值后才会进行下一次写入。

注意，对于一条 UDP 请求，如果尝试写入时处于缓存已满状态，写入操作不会被阻塞，而是**丢弃**，如果设置过低或为 0 可能会导致预期外的宽带浪费。

默认值:

* 在 ARM、MIPS、MIPSLE 平台上，默认值为 `0`。
* 在 ARM64、MIPS64、MIPS64LE 平台上，默认值为 `4`。
* 在其它平台上，默认值为 `512`。

默认值可以通过环境变量 XRAY\_RAY\_BUFFER\_SIZE 设置，注意在环境变量中单位为 MB(环境变量设置为 1 等于 config 设置为 1024)

### SystemPolicyObject

```json
{
  "statsInboundUplink": false,
  "statsInboundDownlink": false,
  "statsOutboundUplink": false,
  "statsOutboundDownlink": false
}
```

> `statsInboundUplink`: true | false

当值为 `true` 时，开启所有入站代理的上行流量统计。

> `statsInboundDownlink`: true | false

当值为 `true` 时，开启所有入站代理的下行流量统计。

> `statsOutboundUplink`: true | false

当值为 `true` 时，开启所有出站代理的上行流量统计。

> `statsOutboundDownlink`: true | false

当值为 `true` 时，开启所有出站代理的下行流量统计。

---

---
url: /config/routing.md
---
# 路由

路由功能模块可以将入站数据按不同规则由不同的出站连接发出，以达到按需代理的目的。

如常见用法是分流国内外流量，Xray 可以通过内部机制判断不同地区的流量，然后将它们发送到不同的出站代理。

有关路由功能更详细的解析：[路由 (routing) 功能简析](../document/level-1/routing-lv1-part1.md)。

## RoutingObject

`RoutingObject` 对应配置文件的 `routing` 项。

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [],
    "balancers": []
  }
}
```

> `domainStrategy`: "AsIs" | "IPIfNonMatch" | "IPOnDemand"

域名解析策略，根据不同的设置使用不同的策略。

* `"AsIs"`：不进行额外操作，使用目标地址里的域名或者 sniff 到的域名。默认值；
* `"IPIfNonMatch"`：一整轮匹配结束后，当没有命中任何规则时，将域名解析成 IP 再次进行二次匹配；
* `"IPOnDemand"`：在开始进行匹配前，直接先将域名解析为 IP 进行匹配；

实际解析行为会被推迟到第一次遇到 IP 规则以降低延迟。结果将同时包含 IPv4 与 IPv6（你可以在内置 DNS 的 `queryStrategy` 进行二次限制） 域名解析出多条 IP 时每条规则将依次尝试全部 IP，只要任意一 IP 符合要求即视为命中规则。

当开启 sniff + routeOnly 使路由系统可以同时看见 IP 和域名时，如果发生上述的解析，路由系统只能看到由域名解析出的 IP 而无法看见原始目标 IP, 除非解析失败。

当存在两个域名时（目标域名 + sniff 结果）， 无论是用于解析还是用于域名匹配，sniff 结果的优先级总是更高。

无论解析与否，路由系统不会影响真正目标地址，请求的目标仍然是原始目标。

> `rules`: \[[RuleObject](#ruleobject)]

对应一个数组，数组中每一项是一个规则。

对于每一个连接，路由将根据这些规则从上到下依次进行判断，当遇到第一个生效规则时，即将这个连接转发至它所指定的 `outboundTag` 或 `balancerTag`。

::: tip
当没有匹配到任何规则时，流量默认由第一个 outbound 发出。
:::

> `balancers`: \[ [BalancerObject](#balancerobject) ]

一个数组，数组中每一项是一个负载均衡器的配置。

当一个规则指向一个负载均衡器时，Xray 会通过此负载均衡器选出一个 outbound, 然后由它转发流量。

### RuleObject

```json
{
  "domain": ["baidu.com", "qq.com", "geosite:cn"],
  "ip": ["0.0.0.0/8", "10.0.0.0/8", "fc00::/7", "fe80::/10", "geoip:cn"],
  "port": "53,443,1000-2000",
  "sourcePort": "53,443,1000-2000",
  "localPort": "53,443,1000-2000",
  "network": "tcp",
  "sourceIP": ["10.0.0.1"],
  "localIP": ["192.168.0.25"],
  "user": ["love@xray.com"],
  "vlessRoute": "53,443,1000-2000",
  "inboundTag": ["tag-vmess"],
  "protocol": ["http", "tls", "quic", "bittorrent"],
  "attrs": { ":method": "GET" },
  "process": ["curl"],
  "outboundTag": "direct",
  "balancerTag": "balancer",
  "ruleTag": "rule name",
  "webhook": {
    "url": "https://api.example.com/alert",
    "deduplication": 300
  }
}
```

::: danger
当多个属性同时指定时，这些属性需要**同时**满足，才可以使当前规则生效。
:::

> `domain`: \[string]

* 纯字符串：同下面的子串，但可以省略前面的 `"keyword:"` 开头。
* 正则表达式：由 `"regexp:"` 开始，余下部分是一个正则表达式。当此正则表达式匹配目标域名时，该规则生效。例如 "regexp:\\\\.goo.\*\\\\.com$" 匹配 "www.google.com"、"fonts.googleapis.com"，但不匹配 "google.com"。大小写敏感。
* 子域名 (推荐)：由 `"domain:"` 开始，余下部分是一个域名。当此域名是目标域名或其子域名时，该规则生效。例如 "domain:xray.com" 匹配 "www.xray.com" 与 "xray.com"，但不匹配 "wxray.com"。
* 子串：由 `"keyword:"` 开始，余下部分是一个字符串。当此字符串匹配目标域名中任意部分，该规则生效。例如 "keyword:sina.com" 可以匹配 "sina.com"、"sina.com.cn" 和 "www.sina.com"，但不匹配 "sina.cn"。
* 完整匹配：由 `"full:"` 开始，余下部分是一个域名。当此域名完整匹配目标域名时，该规则生效。例如 "full:xray.com" 匹配 "xray.com" 但不匹配 "www.xray.com"。
* 无点域名：由 `"dotless:"` 开头，余下部分是一个不能含有 `.` 的字符串。当域名不含 `.` 且此字符串匹配目标域名中任意部分，该规则生效。例如 "dotless:pc-" 可以匹配 "pc-alice"、"mypc-alice"，适用于内网 NetBIOS 域等。大小写敏感。
* 预定义域名列表：由 `"geosite:"` 开头，余下部分是一个名称，如 `geosite:google` 或者 `geosite:cn`。名称及域名列表参考 [预定义域名列表](#预定义域名列表)。
* 从文件中加载域名：形如 `"ext:file:tag"`，必须以 `ext:`（小写）开头，后面跟文件名和标签，文件存放在 [资源目录](./features/env.md#资源文件路径) 中，文件格式与 `geosite.dat` 相同，标签必须在文件中存在。

::: tip
`"ext:geoip.dat:cn"` 等价于 `"geoip:cn"`
:::

> `ip`: \[string]

一个数组，数组内每一项代表一个 IP 范围。当某一项匹配目标 IP 时，此规则生效。有以下几种形式：

* IP：形如 `"127.0.0.1"`。
* [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing)：形如 `"10.0.0.0/8"`，也可以用 `"0.0.0.0/0"` `"::/0"` 来指定所有 IPv4 或者 IPv6。
* 预定义 IP 列表：此列表预置于每一个 Xray 的安装包中，文件名为 `geoip.dat`。使用方式形如 `"geoip:cn"`，必须以 `geoip:`（小写）开头，后面跟双字符国家代码，支持几乎所有可以上网的国家。
  * 特殊值：`"geoip:private"`，包含所有私有地址，如 `127.0.0.1`。
* 从文件中加载 IP：形如 `"ext:file:tag"`，必须以 `ext:`（小写）开头，后面跟文件名和标签，文件存放在 [资源目录](./features/env.md#资源文件路径) 中，文件格式与 `geoip.dat` 相同标签必须在文件中存在。
* 反选 `!` 功能：`"!10.0.0.0/8"` 表示非 `10.0.0.0/8`，`"!geoip:cn"` 表示非 `geoip:cn` 中的结果。多个反选项之间是 `AND` 关系，而正选项、正选项和所有的反选项之间是 `OR` 关系，例如 `ip: ["!geoip:cn", "!geoip:us", "geoip:telegram"]` 匹配非美国并且非中国的 IP，或者是 telegram 的 IP。

> `port`：number | string

目标端口范围，有三种形式：

* `"a-b"`：a 和 b 均为正整数，且小于 65536。这个范围是一个前后闭合区间，当目标端口落在此范围内时，此规则生效。
* `a`：a 为正整数，且小于 65536。当目标端口为 a 时，此规则生效。
* 以上两种形式的混合，以逗号 "," 分隔。形如：`"53,443,1000-2000"`。

> `sourcePort`：number | string

来源端口，有三种形式：

* `"a-b"`：a 和 b 均为正整数，且小于 65536。这个范围是一个前后闭合区间，当目标端口落在此范围内时，此规则生效。
* `a`：a 为正整数，且小于 65536。当目标端口为 a 时，此规则生效。
* 以上两种形式的混合，以逗号 "," 分隔。形如：`"53,443,1000-2000"`。

> `localPort`：number | string

本地入站的端口，格式和 `port`/`sourcePort` 一致，在入站监听一个端口范围时可能有用。

> `network`: "tcp" | "udp" | "tcp,udp"

可选的值有 "tcp"、"udp" 或 "tcp,udp"，当连接方式是指定的方式时，此规则生效。

由于核心很明显只支持 tcp 和 udp 两种四层协议，所以一个仅包含 `"network": "tcp,udp"` 条件的路由可以用于 catch all 匹配任何流量。一个使用例子是放在所有路由规则的最末尾用于指定没有任何其他规则时使用的默认出站（否则核心默认走第一个）

当然其他很明显能匹配任何流量的写法比如指定 1-65535 的 port 或者 0.0.0.0/0 + ::/0 的 ip 也有类似作用

> `sourceIP`: \[string]

一个数组，数组内每一项代表一个 IP 范围，形式有 IP、CIDR、GeoIP 和从文件中加载 IP。当某一项匹配来源 IP 时，此规则生效。

别名: `source`

> `localIP`: \[string]

格式同其他 IP, 用以指定本地入站使用的 IP(使用 0.0.0.0 监听全部 IP 时不同的实际进入 IP 将产生不同的 localIP).

对 UDP 无效(UDP 面向报文的原因无法跟踪), 总是看到 listen 的 IP

> `user`: \[string]

一个数组，数组内每一项是一个邮箱地址。当某一项匹配来源用户时，此规则生效。

类似于域名，其也支持类似 `regexp:` 开头的正则进行匹配。（同样需要替换`\`为`\\`, 见 domain 部分的解释）

> `vlessRoute`: number | string

VLESS 入站会允许配置的 UUID 第七和第八个字节被客户端修改为任何字节，服务端路由会将其作为 vlessRoute 数据，允许用户不更改任何外部字段的情况下根据需求自定义部分服务端路由。

```
--------------↓↓↓↓------------------
xxxxxxxx-xxxx-0000-xxxx-xxxxxxxxxxxx
```

配置中使用的是大端序编码为 uint16 后的数据(听不懂的话，把这四位当成一个十六进制数并化为十进制) 如 `0001→1` `000e→14` `38b2→14514`. 这么做的原因是这里的写法同 `port`, 可以像指定 port 一样自由指定许多段进行路由。

> `inboundTag`: \[string]

一个数组，数组内每一项是一个标识。当某一项匹配入站协议的标识时，此规则生效。

> `protocol`: \[ "http" | "tls" | "quic" | "bittorrent" ]

一个数组，数组内每一项表示一种协议。当某一个协议匹配当前连接的协议类型时，此规则生效。

`http` 仅支持 1.0 和 1.1 暂不支持 h2. (明文 h2 流量也非常少见)

`tls` TLS 1.0 ~ 1.3

`quic` 由于该协议复杂性，嗅探有时可能失效。

`bittorrent` 只有最基础的嗅探，对很多加密和混淆可能不会奏效。

::: tip
必须开启入站代理中的 `sniffing` 选项, 才能嗅探出连接所使用的协议类型.
:::

> `attrs`: object

一个 json object，键名字和值皆为字符串，用于检测 HTTP 流量的属性值(由于显而易见的原因，只支持 1.0 和 1.1)。当 HTTP headers 包含所有指定的键，并且值包含指定的子字符串，则命中此规则。键大小写不敏感。值支持使用正则表达式。

同时也支持类似 h2 的伪头部 `:method` 和 `:path` 用于匹配方法和路径(尽管在 HTTP/1.1 中是不存在这些 header 的)

对于 HTTP 入站的非 CONNECT 方法，可以直接获取到 attrs, 对于其他入站则需要开启 sniffing 嗅探才能获得这些值用于匹配。

示例：

* 检测 HTTP GET：`{":method": "GET"}`
* 检测 HTTP Path：`{":path": "/test"}`
* 检测 Content Type：`{"accept": "text/html"}`

> `process`: \[string]

如果连接来自本机，匹配其进程。如果不来自本机则直接视作匹配失败。仅支持 Windows 和 Linux.

该选项为一个数组，数组内每一项有三种匹配模式。

1. 不包含斜杠，匹配进程名字。
2. 包含斜杠，不以斜杠结尾，匹配绝对路径。
3. 包含斜杠，以斜杠结尾，匹配文件夹，该文件夹下的进程都视为命中。

注：

* 所有选项均大小写敏感。
* Windows 上使用反斜杠 `\` 表示路径，这里统一要求使用普通斜杠 `/`，如：`C:/Windows/System32/curl.exe`，因为反斜杠在 json 中会被视作转义符，使用不便（除非你选择把出现的反斜杠写两遍，如果这么做也可以正常识别）。
* 使用进程名匹配时核心会自动删去 `.exe` 后缀，同样的 `["curl"]` 可以在 Linux 和 Windows 上都命中 curl，使用绝对路径时仍不能忽略 `.exe` 后缀。

特殊语法糖

* `self/` 用于匹配当前核心进程，对于避免回环路由非常有用。
* `xray/` 会被替换为当前核心所在的绝对路径，将会命中所有从该二进制启动的 Xray 进程。

> `outboundTag`: string

对应一个 outbound 的标识。

> `balancerTag`: string

对应一个 Balancer 的标识。

::: tip
`balancerTag` 和 `outboundTag` 须二选一。当同时指定时，`outboundTag` 生效。
:::

> `ruleTag`: string

可选，无实际作用，仅用于标识这条规则的名字

如果设置，则命中该条规则时会在 Info 等级输出相关信息，用于调试路由具体命中了哪条规则。

> `webhook`: [WebhookObject](#webhookobject)

可选。如果设置，则命中该条规则时会向指定 URL 发送 POST 请求。

POST 请求中发送的数据示例：

```json
{
  "email": "2", // string | null
  "level": null, // number | null
  "protocol": "tls", // string | null
  "network": "tcp", // string
  "source": "tcp:127.0.0.1:54203", // string | null
  "destination": "tcp:dns.google:443", // string
  "routeTarget": null, // string | null
  "originalTarget": "tcp:8.8.8.8:443", // string | null
  "inboundTag": "VLESS_TCP", // string | null
  "inboundName": "vless", // string | null
  "inboundLocal": "tcp:192.168.108.1:443", // string | null
  "outboundTag": "notify-bittorrent", // string | null
  "ts": 1771886901 // number
}
```

#### WebhookObject

```json
{
  "url": "http://127.0.0.1:8080/api/webhook",
  "deduplication": 10,
  "headers": {
    "X-API-Key": "your-secret-key"
  }
}
```

> `url`: string

发送通知的目标 URL。支持标准网址和本地 Unix socket 路径。

* `https://api.example.com/alert` — 通过 HTTP(S) 发送通知的标准 URL。用于与外部 Web 服务或 API 集成。
* `/var/run/webhook.sock` — 通过 Unix socket 发送通知，POST 请求将发送到该 socket 的根路径 `/`。
* `/var/run/webhook.sock:/alert` — 通过 Unix socket 向特定端点 `/alert` 发送通知。这允许直接与本地服务集成，无需使用网络接口。
* `@abstract:/webhook` — abstract socket（lock-free，仅 Linux/Android）。
* `@@padded:/webhook` — 带 padding 的 abstract socket，用于 HAProxy 兼容。

> `deduplication`: number

事件去重的时间（单位：秒）。在此时间段内触发的多个相同事件，重复请求将被忽略。

> `headers`: object

HTTP 请求头。

### BalancerObject

负载均衡器配置。当一个负载均衡器生效时，它会从指定的 outbound 中，按配置选出一个最合适的 outbound，进行流量转发。

```json
{
  "tag": "balancer",
  "selector": [],
  "fallbackTag": "outbound",
  "strategy": {}
}
```

> `tag`: string

此负载均衡器的标识，用于匹配 `RuleObject` 中的 `balancerTag`。

> `selector`: \[ string ]

一个字符串数组，其中每一个字符串将用于和 outbound 标识的前缀匹配。在以下几个 outbound 标识中：`[ "a", "ab", "c", "ba" ]`，`"selector": ["a"]` 将匹配到 `[ "a", "ab" ]`。

一般匹配到多个 outbound，使他们均衡的承担负载。

> `fallbackTag`: string

如果根据连接观测结果所有 outbound 都无法连接，则使用这个配置项指定的 outbound。

注意：需要添加 [observatory](./observatory.md#observatoryobject) 或者 [burstObservatory](./observatory.md#burstobservatoryobject) 配置项

> `strategy`: [StrategyObject](#strategyobject)

#### StrategyObject

```json
{
  "type": "roundRobin",
  "settings": {}
}
```

> `type` : "random" | "roundRobin" | "leastPing" | "leastLoad"

* `random` 默认值。随机选择匹配到的出站代理。
* `roundRobin` 按顺序选择匹配到的出站代理。
* `leastPing` 根据连接观测结果选择延迟最小的匹配到的出站代理。需要添加 [observatory](./observatory.md#observatoryobject) 或者 [burstObservatory](./observatory.md#burstobservatoryobject) 配置项。
* `leastLoad` 根据连接观测结果选择最稳定的出站代理。需要添加 [observatory](./observatory.md#observatoryobject) 或者 [burstObservatory](./observatory.md#burstobservatoryobject) 配置项。

::: tip
无论哪一种模式，一旦其所有的 `selector` 对应节点同时配置了 `observatory` 或 `burstObservatory`，则可以过滤出健康节点。若没有任何健康节点可用，会尝试 `fallbackTag`
:::

> `settings`: [StrategySettingsObject](#strategysettingsobject)

##### StrategySettingsObject

这是一个可选配置项，不同负载均衡策略的配置格式有所不同。目前只有 `leastLoad` 负载均衡策略可以添加这个配置项。

```json
{
  "expected": 2,
  "maxRTT": "1s",
  "tolerance": 0.01,
  "baselines": ["1s"],
  "costs": [
    {
      "regexp": false,
      "match": "tag",
      "value": 0.5
    }
  ]
}
```

> `expected`: number

负载均衡器选出最优节点的个数，流量将在这几个节点中随机分配。

> `maxRTT`: string

最高可接受的测速 RTT 时长。

> `tolerance`: float number

最多可接受的测速失败比例，例如 0.01 指可接受百分之一测速失败。（似乎未实现）

> `baselines`: \[ string ]

最高可接受的测速 RTT 标准差时长。

> `costs`: \[ CostObject ]

可选配置项，一个数组，可以给所有出站指定权重。

> `regexp`: true | false

是否用正则表达式选择出站 `Tag`。

> `match`: string

匹配出站 `Tag`。

> `value`: float number

权重值，值越大，对应节点越不易被选中。

### 负载均衡配置示例

```json
    "routing": {
        "rules": [
            {
                "inboundTag": [
                    "in"
                ],
                "balancerTag": "round"
            }
        ],
        "balancers" : [
            {
                "selector": [
                    "out"
                ],
                "strategy": {
                    "type":"roundRobin"
                },
                "tag": "round"
            }
        ]
    },

    "inbounds": [
        {
            // 入站配置
            "tag": "in"
        }
    ],

    "outbounds": [
        {
            // 出站配置
            "tag": "out1"
        },
        {
            // 出站配置
            "tag": "out2"
        }
    ]
```

### 预定义域名列表

此列表预置于每一个 Xray 的安装包中，文件名为 `geosite.dat`。这个文件包含了一些常见的域名，使用方式：`geosite:xxx`，如 `geosite:google` 表示对文件内符合 `google` 内包含的域名，进行路由筛选或 DNS 筛选。

常见的域名有：

* `category-ads`：包含了常见的广告域名。
* `category-ads-all`：包含了常见的广告域名，以及广告提供商的域名。
* `cn`：相当于 `geolocation-cn` 和 `tld-cn` 的合集。
* `apple`：包含了 Apple 旗下绝大部分域名。
* `google`：包含了 Google 旗下绝大部分域名。
* `microsoft`：包含了 Microsoft 旗下绝大部分域名。
* `facebook`：包含了 Facebook 旗下绝大部分域名。
* `twitter`：包含了 Twitter 旗下绝大部分域名。
* `telegram`：包含了 Telegram 旗下绝大部分域名。
* `geolocation-cn`：包含了常见的大陆站点域名。
* `geolocation-!cn`：包含了常见的非大陆站点域名。
* `tld-cn`：包含了 CNNIC 管理的用于中国大陆的顶级域名，如以 `.cn`、`.中国` 结尾的域名。
* `tld-!cn`：包含了非中国大陆使用的顶级域名，如以 `.tw`（台湾）、`.jp`（日本）、`.sg`（新加坡）、`.us`（美国）`.ca`（加拿大）等结尾的域名。

你也可以在这里查看完整的域名列表 [Domain list community](https://github.com/v2fly/domain-list-community)。

---

---
url: /config/stats.md
---
# 统计信息

用于配置 Xray 流量数据的统计。

## StatsObject

`StatsObject` 对应配置文件的 `stats` 项。

```json
{
  "stats": {}
}
```

目前统计信息不需要任何参数，只要 `StatsObject` 项存在，内部的统计即会开启。

开启了统计以后, 只需在 [Policy](./policy.md) 中开启对应的项，就可以统计对应的数据。

## 获取统计信息

可以用 `xray api` 的相关命令获取统计信息.

目前已有的统计信息如下：

* 用户数据
  * `user>>>[email]>>>traffic>>>uplink`

    特定用户的上行流量，单位字节。

  * `user>>>[email]>>>traffic>>>downlink`

    特定用户的下行流量，单位字节。

::: tip
如果对应用户没有指定 Email，则不会开启统计。
:::

* 全局数据
  * `inbound>>>[tag]>>>traffic>>>uplink`

    特定 inbound 的上行流量，单位字节。

  * `inbound>>>[tag]>>>traffic>>>downlink`

    特定 inbound 的下行流量，单位字节。

  * `outbound>>>[tag]>>>traffic>>>uplink`

    特定 outbound 的上行流量，单位字节。

  * `outbound>>>[tag]>>>traffic>>>downlink`

    特定 outbound 的下行流量，单位字节。

---

---
url: /config/transport.md
---
# 传输方式（uTLS、REALITY）

传输方式（transport）是当前 Xray 节点和其它节点对接的方式。

传输方式指定了稳定的数据传输的方式。通常来说，一个网络连接的两端需要有对称的传输方式。比如一端用了 WebSocket，那么另一个端也必须使用 WebSocket，否则无法建立连接。

## StreamSettingsObject

`StreamSettingsObject` 对应 [`InboundObject`](./inbound.md) 或 [`OutboundObject`](./outbound.md) 中的 `streamSettings` 项。每一个入站或出站都可以分别配置不同的传输配置，都可以设置 `streamSettings` 来进行一些传输的配置。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        // [!code focus:34]
        "network": "raw",
        "security": "none",
        "tlsSettings": {},
        "realitySettings": {},
        "rawSettings": {},
        "xhttpSettings": {},
        "kcpSettings": {},
        "grpcSettings": {},
        "wsSettings": {},
        "httpupgradeSettings": {},
        "hysteriaSettings": {},
        "finalmask": {
          "tcp": [],
          "udp": [],
          "quicParams": {}
        },
        "sockopt": {
          "mark": 0,
          "tcpMaxSeg": 1440,
          "tcpFastOpen": false,
          "tproxy": "off",
          "domainStrategy": "AsIs",
          "happyEyeballs": {},
          "dialerProxy": "",
          "acceptProxyProtocol": false,
          "tcpKeepAliveInterval": 0,
          "tcpKeepAliveIdle": 300,
          "tcpUserTimeout": 10000,
          "tcpCongestion": "bbr",
          "interface": "wg0",
          "v6only": false,
          "tcpWindowClamp": 600,
          "tcpMptcp": false
        }
      }
    }
  ]
}
```

> `network`: "raw" | "xhttp" | "kcp" | "grpc" | "ws" | "httpupgrade" | "hysteria"

连接的数据流所使用的传输方式类型，默认值为 `"raw"`。

::: tip
v24.9.30 版本后，为了更贴近实际行为，TCP 传输方式已更名为 RAW。为了兼容性，`"network": "raw"` 和 `"network": "tcp"`，`rawSettings` 和 `tcpSettings` 互为别名。
:::

> `security`: "none" | "tls" | "reality"

是否启用传输层加密，支持的选项有

* `"none"` 表示不加密（默认值）
* `"tls"` 表示使用 [TLS](https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E6%80%A7%E5%8D%94%E5%AE%9A)。
* `"reality"` 表示使用 REALITY。

> `tlsSettings`: [TLSObject](#tlsobject)

TLS 配置。TLS 由 Golang 提供，通常情况下 TLS 协商的结果为使用 TLS 1.3，不支持 DTLS。

> `realitySettings`: [RealityObject](#realityobject)

Reality 配置。Reality 是 Xray 的原创黑科技。 Reality 比 TLS 的安全性更高, 配置方式也和 TLS 一致.

::: tip
Reality 是目前最安全的传输加密方案, 且外部看来流量类型和正常上网具有一致性。 启用 Reality 并且配置合适的 XTLS Vision 流控模式, 可以
达到数倍甚至十几倍的性能提升。
:::

> `rawSettings`: [RawObject](./transports/raw.md)

当前连接的 RAW 配置，仅当此连接使用 RAW 时有效。

> `xhttpSettings`: [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

当前连接的 XHTTP 配置，仅当此连接使用 XHTTP 时有效。

> `kcpSettings`: [KcpObject](./transports/mkcp.md)

当前连接的 mKCP 配置，仅当此连接使用 mKCP 时有效。

> `grpcSettings`: [GRPCObject](./transports/grpc.md)

当前连接的 gRPC 配置，仅当此连接使用 gRPC 时有效。

> `wsSettings`: [WebSocketObject](./transports/websocket.md)

当前连接的 WebSocket 配置，仅当此连接使用 WebSocket 时有效。

> `httpupgradeSettings`: [HttpUpgradeObject](./transports/httpupgrade.md)

当前连接的 HTTPUpgrade 配置，仅当此连接使用 HTTPUpgrade 时有效。

> `hysteriaSettings`: [HysteriaObject](./transports/hysteria.md)

当前连接的 Hysteria 配置，仅当此连接使用 Hysteria 时有效。

> `sockopt`: [SockoptObject](#sockoptobject)

透明代理相关的具体配置。

> `finalmask`: [FinalMaskObject](#finalmaskobject)

FinalMask 配置，用于对流量进行通用伪装。

### TLSObject

```json
{
  "serverName": "xray.com",
  "verifyPeerCertByName": "",
  "rejectUnknownSni": false,
  "allowInsecure": false,
  "alpn": ["h2", "http/1.1"],
  "minVersion": "1.2",
  "maxVersion": "1.3",
  "cipherSuites": "此处填写你需要的加密套件名称,每个套件名称之间用:进行分隔",
  "certificates": [],
  "disableSystemRoot": false,
  "enableSessionResumption": false,
  "fingerprint": "",
  "pinnedPeerCertSha256": "",
  "curvePreferences": [""],
  "masterKeyLog": "",
  "echServerKeys": "",
  "echConfigList": "",
  "echSockopt": {}
}
```

> `serverName`: string

服务器名称，服务器端证书的 SAN 中需要包含该值，可以是域名或者 IP 地址。当为域名时将在 Client Hello 中的 SNI 扩展中发送，IP 地址则不会发送 SNI 扩展（SNI 扩展不允许包含 IP 地址）。如果填入 IPv6 要使用 `[]` 包裹

当留空时，自动使用 address 中的值（如果是域名）。

特殊值 `"FromMitM"`, 这会使其使用入来自 dokodemo-door 入站解密的 TLS 中包含的 SNI.

> `verifyPeerCertByName`: string

仅客户端，用于校验证书使用的 SNI，可以用 `,` 分割多个域名(只需要证书中有一个 SAN 在该列表中即可), 将会覆盖本用于校验的 `serverName`, 用于域前置等特殊目的。

特殊值 `"FromMitM"`, 这会使其额外加入来自 dokodemo-door 入站解密的 TLS 中包含的 SNI.

> `rejectUnknownSni`: bool

当值为 `true` 时，服务端接收到的 SNI 与证书域名不匹配即拒绝 TLS 握手，默认为 false。

> `alpn`: \[ string ]

一个字符串数组，指定了 TLS 握手时指定的 ALPN 数值。默认值为 `["h2", "http/1.1"]`。

特殊值：`["FromMitM"]` (有且仅有这一个元素时) 会使出站 TLS 使用来自 dokodemo-door 入站解密的 TLS 连接使用的 alpn.

> `minVersion`: string

minVersion 为可接受的最小 TLS 版本。

> `maxVersion`: string

maxVersion 为可接受的最大 TLS 版本。

> `cipherSuites`: string

CipherSuites 用于配置受支持的密码套件列表, 每个套件名称之间用:进行分隔.

你可以在 [这里](https://golang.org/src/crypto/tls/cipher_suites.go#L500)或 [这里](https://golang.org/src/crypto/tls/cipher_suites.go#L44)
找到 golang 加密套件的名词和说明

::: danger
以上两项配置为非必要选项，正常情况下不影响安全性 在未配置的情况下 golang 根据设备自动选择. 若不熟悉, 请勿配置此选项, 填写不当引起的问题自行负责
:::

> `allowInsecure`: true | false

是否允许不安全连接（仅用于客户端）。默认值为 `false`。

当值为 `true` 时，Xray 不会检查远端主机所提供的 TLS 证书的有效性。

::: danger
\~~出于安全性考虑，这个选项不应该在实际场景中选择 true，否则可能遭受中间人攻击。~~

该选项已被弃用，使用 `pinnedPeerCertSha256` 手动指定需要的证书。
:::

> `disableSystemRoot`: true | false

是否禁用操作系统自带的 CA 证书。默认值为 `false`。

当值为 `true` 时，Xray 只会使用 `certificates` 中指定的证书进行 TLS 握手。当值为 `false` 时，Xray 只会使用操作系统自带的 CA 证书进行 TLS 握手。

> `enableSessionResumption`: true | false

是否启用会话恢复，默认禁用，只有服务端和客户端都启用时候才会尝试协商会话恢复。

如果协商成功将可以不在握手过程中传输证书。稍微节省一点点握手时间（几乎可以忽略不计）

注意，这不是 TLS 0RTT, gotls 尚未支持此功能，这不会减少 TLS 握手的 RTT.

> `fingerprint` : string

此参数用于配置指定 `TLS Client Hello` 的指纹。默认值为 `chrome` 要恢复为原生 go TLS, 请设置为 `unsafe`. 启用后，Xray 将通过 uTLS 库 **模拟** `TLS` 指纹，或随机生成。支持三种配置方式：

1. 常见浏览器最新版本的 TLS 指纹 包括

* `"chrome"`
* `"firefox"`
* `"safari"`
* `"ios"`
* `"android"`
* `"edge"`
* `"360"`
* `"qq"`

2. 在 xray 启动时自动生成一个指纹

* `"random"`: 在较新版本的浏览器里随机抽取一个
* `"randomized"`: 完全随机生成一个独一无二的指纹 (100% 支持 TLS 1.3 使用 X25519)

3. 使用 uTLS 原生指纹变量名 例如`"HelloRandomizedNoALPN"` `"HelloChrome_106_Shuffle"`。完整名单见 [uTLS 库](https://github.com/refraction-networking/utls/blob/master/u_common.go#L434)

::: tip
此功能仅 **模拟** `TLS Client Hello` 的指纹，行为、其他指纹与 Golang 相同。如果你希望更加完整地模拟浏览器 `TLS`
指纹与行为，可以使用 [Browser Dialer](./transports/websocket.md#browser-dialer)。
:::

::: tip
当使用此功能时，TLS 的部分影响TLS指纹的选项将被 utls 库覆盖不再生效，列如ALPN。
会被传递的参数有
`"serverName" "disableSystemRoot" "pinnedPeerCertSha256" "masterKeyLog"`
:::

> `pinnedPeerCertSha256`: string

用于指定远程服务器的证书 SHA256 散列值，使用 hex 且大小写不敏感。如 `e8e2d387fdbffeb38e9c9065cf30a97ee23c0e3d32ee6f78ffae40966befccc9`，可以使用 `,` 连接更多的散列值，匹配到任何一个即通过验证。

该编码与 Chrome 证书查看器 SHA-256 证书指纹，以及 crt.sh 的 Certificate Fingerprints SHA-256 格式均相同。可以使用 `xray tls hash --cert <cert.pem>` 进行计算，也可以使用 `openssl x509 -noout -fingerprint -sha256 -in cert.pem` （兼容它生成的带冒号的格式）， `xray tls ping` 同样会输出远程证书的 SHA256 散列值。

该验证将覆盖默认的证书校验，分两种情况：

* 1.当核心找到匹配的散列值为叶子证书，验证直接通过。
* 2.当核心找到匹配的值为 CA 证书（可以是根证书也可以是中级证书），将使用 `serverName` 里的值验证叶子证书上的签名是否来自该 CA 授权。

> `certificates`: \[ [CertificateObject](#certificateobject) ]

证书列表，其中每一项表示一个证书（建议 fullchain）。

::: tip
如果要在 ssllibs 或者 myssl 获得 A/A+ 等级的评价,
请参考 [这里](https://github.com/XTLS/Xray-core/discussions/56#discussioncomment-215600).
:::

> `curvePreferences`: \[ string ]

一个字符串数组，指定 TLS 握手执行ECDHE时支持的曲线。支持的曲线列表如下（大小写不敏感）.

```
CurveP256
CurveP384
CurveP521
X25519
X25519MLKEM768
SecP256r1MLKEM768*
SecP384r1MLKEM1024*
```

\*: 未被 utls 支持

默认值截止至 go1.26 为包含上述全部曲线。调整顺序并不会使客户端或者服务器偏好使用哪种曲线，实际曲线将由密钥交换机制自行协商。

> `masterKeyLog` : string

(Pre)-Master-Secret log 文件路径，可用于Wireshark等软件解密Xray发送的TLS连接。

> `echServerKeys` : string

仅服务端参数，用于服务端启用 Encrypted Client Hello.

使用 `xray tls ech --serverName example.com` 生成可用的 ECH Server Key 和对应的 Config, 其中 example.com 是在 SNI 被加密用用于暴露在外部的 SNI, 可以随便填。Server Key 包含了 ECHConfig, 如果你不慎弄丢了客户端用的 Config 可以使用 `xray tls ech -i "你的 server key"` 重新获得。你可以把它发布到 DNS 的 HTTPS 记录中，格式参考[这里](https://dns.google/query?name=encryptedsni.com\&rr_type=HTTPS) 或者 RFC 9460

注意服务端配置 ECH 后仍然接受正常的非 ECH 连接。

> `echConfigList` : string

仅客户端参数，配置 ECHConfig, 不为空则代表客户端启用 Encrypted Client Hello. 支持两种格式

第一种直接 固定 ECHConfig, 如 `"AF7+DQBaAAAgACA51i3Ssu4wUMV4FNCc8iRX5J+YC4Bhigz9sacl2lCfSQAkAAEAAQABAAIAAQADAAIAAQACAAIAAgADAAMAAQADAAIAAwADAAtleGFtcGxlLmNvbQAA"`

第二种从 DNS 服务器查询，比方说使用 CDN 时可以通过 HTTPS 记录动态获取其配置的 ECHConfig, 如果获取到有效 ECH Config, Xray 会遵守服务器下发的 TTL，查询目标会是配置的 SNI, 或者配置的服务器域名(如果 SNI 为空且目标为一个域名)

基础格式为 `"udp://1.1.1.1"` 表示从 UDP DNS 1.1.1.1 查询，也可以使用 `"https://1.1.1.1/dns-query"`(或者 `h2c://`) 这样的格式，代表使用 DOH(h2c) 进行查询(实际使用请替换成当地可用的服务器). 上述三种均支持修改端口号，如 `udp://1.1.1.1:53`，没写会按照协议默认 53/443.

特别地，可以使用指定的域名用于查询 ECHConfig, 格式为 `"example.com+https://1.1.1.1/dns-query"` 这样 Xray 会强制使用 example.com 的 DNS 记录中的 ECHConfig 用于连接，如果你想从 DNS 获取 ECHConfig 但又不想暴露自己在查询这个域名的 HTTPS 记录或者在这个域名下发布 HTTPS 记录时有一些用。

> `echSockopt` : [SockoptObject](#sockoptobject)

调整使用 DNS 查询 ECH 记录时使用的连接的底层 socket 选项。

### RealityObject

```json
{
  "show": false,
  "target": "example.com:443",
  "xver": 0,
  "serverNames": ["example.com", "www.example.com"],
  "privateKey": "",
  "minClientVer": "",
  "maxClientVer": "",
  "maxTimeDiff": 0,
  "shortIds": ["", "0123456789abcdef"],
  "mldsa65Seed": "",
  "limitFallbackUpload": {
    "afterBytes": 0,
    "bytesPerSec": 0,
    "burstBytesPerSec": 0
  },
  "limitFallbackDownload": {
    "afterBytes": 0,
    "bytesPerSec": 0,
    "burstBytesPerSec": 0
  },
  "fingerprint": "chrome",
  "serverName": "",
  "password": "",
  "shortId": "",
  "mldsa65Verify": "",
  "spiderX": ""
}
```

::: tip
更多信息请参考 [REALITY 项目](https://github.com/XTLS/REALITY).
:::

::: tip
Reality 只是修改了TLS，客户端的实现只需要轻度修改完全随机的 session id 和自定义证书验证即可，理论上与大多数 TLS 组合完全兼容。
:::

> `show` : true | false

当值为 `true` 时，输出调试信息。

::: tip
以下为**入站**（**服务端**）配置。
:::

> `target` : string

必填，格式同 VLESS `fallbacks` 的 [dest](./features/fallback.md#fallbackobject)。

旧称 dest, 当前版本两个字段互为alias

如果 target 支持后量子密钥交换算法 X25519MLKEM768, 那么 reality 客户端也会自动使用该后量子算法进行密钥协商。具体是否支持可以使用 `xray tls ping cloudflare.com` (网址更改为dest, 可以带端口号) 检查。

核心按照这个字段是否存在区分是当前是客户端还是服务端配置，不要在客户端填写，否则会造成识别异常。

::: warning
为了伪装的效果考虑，Xray对于鉴权失败（非合法reality请求）的流量，会**直接转发**至 target.
如果 target 网站的 IP 地址特殊（如使用了 CloudFlare CDN 的网站） 则相当于你的服务器充当了 CloudFlare 的端口转发，可能造成被扫描后偷跑流量的情况。

为了杜绝这种情况，可以考虑前置 Nginx 等方法过滤掉不符合要求的 SNI。
或者也可以考虑配置 `limitFallbackUpload` 和 `limitFallbackDownload`，限制其速率。
:::

> `xver` : number

选填，格式同 VLESS `fallbacks` 的 [xver](./features/fallback.md#fallbackobject)

> `serverNames` : \[string]

必填，客户端可用的 `serverName` 列表，不支持 \* 通配符。

一般与 target 保持一致即可，实际的可选值为服务器所接受的任何 SNI（依据 target 本身的配置有所不同），一般是参考是所返回证书的 [SAN](https://zh.wikipedia.org/wiki/%E4%B8%BB%E9%A2%98%E5%A4%87%E7%94%A8%E5%90%8D%E7%A7%B0).

其中可包含空值 `""` 代表接受没有SNI的连接。使用此特性不要求 `target` 具有 IP 证书，只需确保在收到无 SNI 的 Client Hello 其不会拒绝连接。使用这一特性时客户端 `serverName` 不能为空，需要填入任意有效 IP 地址占位。

可以使用 `xray tls ping` 观察服务端对无 SNI 请求的响应行为。

> `privateKey` : string

必填，执行 `./xray x25519` 生成。

> `minClientVer` : string

选填，客户端 Xray 最低版本，格式为 `x.y.z`。

> `maxClientVer` : string

选填，客户端 Xray 最高版本，格式为 `x.y.z`。

> `maxTimeDiff` : number

选填，允许的最大时间差，单位为毫秒。

> `shortIds` : \[string]

必填，客户端可用的 `shortId` 列表，可用于区分不同的客户端。

格式要求见 `shortId`

若包含空值，客户端 `shortId` 可为空。

> `mldsa65Seed` : string

仅服务端，为发送给 Reality 客户端的证书添加额外的后量子签名所使用的私钥，使用 ML-DSA-65 (如果存在可以破解 x25519 的量子计算机，password 泄露可能导致连接可以被 mitm, 该功能可以防止未来的这种攻击)

使用 `xray mldsa65` 生成使用的公私钥对，服务端配置私钥后只会在证书扩展中添加，不影响旧版客户端或没启用该功能的客户端。

注意，配置该功能后 target 所返回的证书长度**必须**大于 3500, 因为后量子签名会导致 Reality 返回的临时证书变大，为了防止产生特征 target 返回的证书也要很大。 可以使用 `xray tls ping example.com` 进行查看检查。同时为了完美的后量子安全，target 也需要支持后量子密钥交换 X25519MLKEM768, 支持情况一样可以通过前面的命令查看。

> `limitFallbackUpload`/`limitFallbackDownload`

::: warning
警告：对于 REALITY 最佳实践始终是偷同 ASN 的证书，那么你大概率用不到此功能；只有当你迫不得已偷了 Cloudflare 这种免费 CDN 的证书时，为避免你服务器成为别人加速节点时可考虑开启此功能。

回落限速是一种特征，不建议启用，如果您是面板/一键脚本开发者，务必让这些参数随机化。
:::

::: tip
`limitFallbackUpload` 和 `limitFallbackDownload` 为选填，可对未通过验证的回落连接限速，`bytesPerSec` 默认为 0 即不启用。

原理：针对每个未通过验证的回落连接，当传输了 afterBytes 字节后开启限速算法。
限速采用令牌桶算法，桶的容量是 burstBytesPerSec，每传输一个字节用掉一个令牌，初始 burstBytesPerSec 是满的。
每秒以 bytesPerSec 个令牌填充桶，直到容量满。

举例：`afterBytes=10485760`, `burstBytesPerSec=5242880`, `bytesPerSec=1048576` 代表传输 15MB 后开始限速为 1MB/s，如果暂停传输，5 秒后能突发到 5MB/s，然后又恢复到 1MB/s。

建议：过大的 `afterBytes` 和 `burstBytesPerSec` 将起不到限速效果，过小的 `bytesPerSec` 和 `burstBytesPerSec` 则十分容易被探测。
应结合被偷网站的资源大小合理设置参数，如果不允许突发，可以把 `burstBytesPerSec` 设为 0。
:::

> `afterBytes` : number

选填，对回落的 REALITY 连接限速，限制传输指定字节后开始限速，默认为 0。

> `bytesPerSec` : number

选填，对回落的 REALITY 连接限速，限制基准速率（字节/秒），默认为 0 即不启用限速功能。

> `burstBytesPerSec` : number

选填，对回落的 REALITY 连接限速，限制突发速率（字节/秒），大于 `bytesPerSec` 时生效。

::: tip
以下为**出站**（**客户端**）配置。
:::

> `serverName` : string

服务端 `serverNames` 之一。

特别地，客户端可以将其设置为任意 IP 地址，Xray 将会发送无 SNI 扩展的 Client Hello. 要使用这一特性请确保服务端 `serverNames` 中包含空值 `""`。

> `fingerprint` : string

必填，同 [TLSObject](#tlsobject)。 注意：此处不支持使用 `unsafe` 禁用 utls, 因为 REALITY 协议实现使用了该库以操作底层 TLS 参数。

> `shortId` : string

服务端 shortIds 之一。

长度为 8 个字节，即 16 个 0~f 的数字字母，可以小于16个，核心将会自动在后面补0, 但位数必须是**偶数** (因为一个字节有2位16进制数)

如 `aa1234` 会被自动补全为 `aa12340000000000`, 但是`aaa1234` 则会导致错误。

0也是偶数，所以若服务端的 `shordIDs` 包含空值 `""` ，客户端也可为空。

> `password` : string

必填，服务端私钥对应的公钥。使用 `./xray x25519 -i "服务器私钥"` 生成。旧称 publicKey, 为防止误解更名(这个东西地位上确实是 x25519 公钥但是在 Reality 的设计中是客户端持有，不能公开)

> `mldsa65Verify`

可选，mldsa65 签名验证使用的公钥，非空时使用该公钥检查服务端返回的证书，详情见 `"mldsa65Seed"` 的描述。

> `spiderX` : string

爬虫初始路径与参数，建议每个客户端不同。

#### CertificateObject

```json
{
  "ocspStapling": 0,
  "oneTimeLoading": false,
  "usage": "encipherment",
  "buildChain": false,
  "certificateFile": "/path/to/certificate.crt",
  "keyFile": "/path/to/key.key",
  "certificate": [
    "--BEGIN CERTIFICATE--",
    "MIICwDCCAaigAwIBAgIRAO16JMdESAuHidFYJAR/7kAwDQYJKoZIhvcNAQELBQAw",
    "ADAeFw0xODA0MTAxMzU1MTdaFw0xODA0MTAxNTU1MTdaMAAwggEiMA0GCSqGSIb3",
    "DQEBAQUAA4IBDwAwggEKAoIBAQCs2PX0fFSCjOemmdm9UbOvcLctF94Ox4BpSfJ+",
    "3lJHwZbvnOFuo56WhQJWrclKoImp/c9veL1J4Bbtam3sW3APkZVEK9UxRQ57HQuw",
    "OzhV0FD20/0YELou85TwnkTw5l9GVCXT02NG+pGlYsFrxesUHpojdl8tIcn113M5",
    "pypgDPVmPeeORRf7nseMC6GhvXYM4txJPyenohwegl8DZ6OE5FkSVR5wFQtAhbON",
    "OAkIVVmw002K2J6pitPuJGOka9PxcCVWhko/W+JCGapcC7O74palwBUuXE1iH+Jp",
    "noPjGp4qE2ognW3WH/sgQ+rvo20eXb9Um1steaYY8xlxgBsXAgMBAAGjNTAzMA4G",
    "A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAA",
    "MA0GCSqGSIb3DQEBCwUAA4IBAQBUd9sGKYemzwPnxtw/vzkV8Q32NILEMlPVqeJU",
    "7UxVgIODBV6A1b3tOUoktuhmgSSaQxjhYbFAVTD+LUglMUCxNbj56luBRlLLQWo+",
    "9BUhC/ow393tLmqKcB59qNcwbZER6XT5POYwcaKM75QVqhCJVHJNb1zSEE7Co7iO",
    "6wIan3lFyjBfYlBEz5vyRWQNIwKfdh5cK1yAu13xGENwmtlSTHiwbjBLXfk+0A/8",
    "r/2s+sCYUkGZHhj8xY7bJ1zg0FRalP5LrqY+r6BckT1QPDIQKYy615j1LpOtwZe/",
    "d4q7MD/dkzRDsch7t2cIjM/PYeMuzh87admSyL6hdtK0Nm/Q",
    "--END CERTIFICATE--"
  ],
  "key": [
    "--BEGIN RSA PRIVATE KEY--",
    "MIIEowIBAAKCAQEArNj19HxUgoznppnZvVGzr3C3LRfeDseAaUnyft5SR8GW75zh",
    "bqOeloUCVq3JSqCJqf3Pb3i9SeAW7Wpt7FtwD5GVRCvVMUUOex0LsDs4VdBQ9tP9",
    "GBC6LvOU8J5E8OZfRlQl09NjRvqRpWLBa8XrFB6aI3ZfLSHJ9ddzOacqYAz1Zj3n",
    "jkUX+57HjAuhob12DOLcST8np6IcHoJfA2ejhORZElUecBULQIWzjTgJCFVZsNNN",
    "itieqYrT7iRjpGvT8XAlVoZKP1viQhmqXAuzu+KWpcAVLlxNYh/iaZ6D4xqeKhNq",
    "IJ1t1h/7IEPq76NtHl2/VJtbLXmmGPMZcYAbFwIDAQABAoIBAFCgG4phfGIxK9Uw",
    "qrp+o9xQLYGhQnmOYb27OpwnRCYojSlT+mvLcqwvevnHsr9WxyA+PkZ3AYS2PLue",
    "C4xW0pzQgdn8wENtPOX8lHkuBocw1rNsCwDwvIguIuliSjI8o3CAy+xVDFgNhWap",
    "/CMzfQYziB7GlnrM6hH838iiy0dlv4I/HKk+3/YlSYQEvnFokTf7HxbDDmznkJTM",
    "aPKZ5qbnV+4AcQfcLYJ8QE0ViJ8dVZ7RLwIf7+SG0b0bqloti4+oQXqGtiESUwEW",
    "/Wzi7oyCbFJoPsFWp1P5+wD7jAGpAd9lPIwPahdr1wl6VwIx9W0XYjoZn71AEaw4",
    "bK4xUXECgYEA3g2o9WqyrhYSax3pGEdvV2qN0VQhw7Xe+jyy98CELOO2DNbB9QNJ",
    "8cSSU/PjkxQlgbOJc8DEprdMldN5xI/srlsbQWCj72wXxXnVnh991bI2clwt7oYi",
    "pcGZwzCrJyFL+QaZmYzLxkxYl1tCiiuqLm+EkjxCWKTX/kKEFb6rtnMCgYEAx0WR",
    "L8Uue3lXxhXRdBS5QRTBNklkSxtU+2yyXRpvFa7Qam+GghJs5RKfJ9lTvjfM/PxG",
    "3vhuBliWQOKQbm1ZGLbgGBM505EOP7DikUmH/kzKxIeRo4l64mioKdDwK/4CZtS7",
    "az0Lq3eS6bq11qL4mEdE6Gn/Y+sqB83GHZYju80CgYABFm4KbbBcW+1RKv9WSBtK",
    "gVIagV/89moWLa/uuLmtApyEqZSfn5mAHqdc0+f8c2/Pl9KHh50u99zfKv8AsHfH",
    "TtjuVAvZg10GcZdTQ/I41ruficYL0gpfZ3haVWWxNl+J47di4iapXPxeGWtVA+u8",
    "eH1cvgDRMFWCgE7nUFzE8wKBgGndUomfZtdgGrp4ouLZk6W4ogD2MpsYNSixkXyW",
    "64cIbV7uSvZVVZbJMtaXxb6bpIKOgBQ6xTEH5SMpenPAEgJoPVts816rhHdfwK5Q",
    "8zetklegckYAZtFbqmM0xjOI6bu5rqwFLWr1xo33jF0wDYPQ8RHMJkruB1FIB8V2",
    "GxvNAoGBAM4g2z8NTPMqX+8IBGkGgqmcYuRQxd3cs7LOSEjF9hPy1it2ZFe/yUKq",
    "ePa2E8osffK5LBkFzhyQb0WrGC9ijM9E6rv10gyuNjlwXdFJcdqVamxwPUBtxRJR",
    "cYTY2HRkJXDdtT0Bkc3josE6UUDvwMpO0CfAETQPto1tjNEDhQhT",
    "--END RSA PRIVATE KEY--"
  ]
}
```

服务端证书，每隔 3600 秒(即一小时)将进行热重载。

> `ocspStapling`: number

OCSP 装订更新间隔，单位为秒，默认值为 0. 任意非 0 值将启用 OCSP 装订且覆盖默认的 3600 秒证书热重载时间(重载的同时执行 OCSP 装订)。

> `oneTimeLoading`: true | false

仅加载一次，默认 false. 值为 `true` 时将关闭证书热重载功能与 OCSP 装订功能。

> `usage`: "encipherment" | "verify" | "issue"

证书用途，默认值为 `"encipherment"`。

* `"encipherment"`：证书用于 TLS 认证和加密。
* `"verify"`：证书用于验证远端 TLS 的证书。当使用此项时，当前证书必须为 CA 证书。
* `"issue"`：证书用于签发其它证书。当使用此项时，当前证书必须为 CA 证书。

::: tip TIP 1
在 Windows 平台上可以将自签名的 CA 证书安装到系统中，即可验证远端 TLS 的证书。
:::

::: tip TIP 2
当有新的客户端请求时，假设所指定的 `serverName` 为 `"xray.com"`，Xray 会先从证书列表中寻找可用于 `"xray.com"` 的证书，如果没有找到，则使用任一 `usage`
为 `"issue"` 的证书签发一个适用于 `"xray.com"` 的证书，有效期为一小时。并将新的证书加入证书列表，以供后续使用。
:::

::: tip TIP 3
当 `certificateFile` 和 `certificate` 同时指定时，Xray 优先使用 `certificateFile`。`keyFile` 和 `key` 也一样。
:::

::: tip TIP 4
当 `usage` 为 `"verify"` 时，`keyFile` 和 `key` 可均为空。
:::

::: tip TIP 5
使用 `xray tls cert` 可以生成自签名的 CA 证书。
:::

::: tip TIP 6
如已经拥有一个域名, 可以使用工具便捷的获取免费第三方证书,如[acme.sh](https://github.com/acmesh-official/acme.sh)
:::

> `buildChain`: true | false

仅当证书用途为 `issue` 时生效，若值为 `true` ，签发证书时将CA证书嵌入证书链。

::: tip TIP 1
不应该将根证书嵌入证书链。该选项只适合在签名CA证书为中间证书时启用。
:::

> `certificateFile`: string

证书文件路径，如使用 OpenSSL 生成，后缀名为 .crt。

> `certificate`: \[ string ]

一个字符串数组，表示证书内容，格式如样例所示。`certificate` 和 `certificateFile` 二者选一。

> `keyFile`: string

密钥文件路径，如使用 OpenSSL 生成，后缀名为 .key。目前暂不支持需要密码的 key 文件。

> `key`: \[ string ]

一个字符串数组，表示密钥内容，格式如样例如示。`key` 和 `keyFile` 二者选一。

### SockoptObject

```json
{
  "mark": 0,
  "tcpMaxSeg": 1440,
  "tcpFastOpen": false,
  "tproxy": "off",
  "domainStrategy": "AsIs",
  "happyEyeballs": {},
  "dialerProxy": "",
  "acceptProxyProtocol": false,
  "tcpKeepAliveInterval": 0,
  "tcpKeepAliveIdle": 300,
  "tcpUserTimeout": 10000,
  "tcpcongestion": "bbr",
  "interface": "wg0",
  "V6Only": false,
  "tcpWindowClamp": 600,
  "tcpMptcp": false,
  "addressPortStrategy": "",
  "customSockopt": []
}
```

> `mark`: number

一个整数。当其值非零时，在 outbound 连接上以此数值标记 SO\_MARK。

* 仅适用于 Linux 系统。
* 需要 CAP\_NET\_ADMIN 权限。

> `tcpMaxSeg`: number

用于设置 TCP 数据包的最大传输单元。

> `tcpFastOpen`: true | false | number

是否启用 [TCP Fast Open](https://zh.wikipedia.org/wiki/TCP%E5%BF%AB%E9%80%9F%E6%89%93%E5%BC%80)。

当其值为 `true` 或`正整数`时，启用 TFO；当其值为 `false` 或`负数`时，强制关闭 TFO；当此项不存在或为 `0` 时，使用系统默认设置。 可用于 inbound/outbound。

* 仅在以下版本（或更新版本）的操作系统中可用:
  * Linux 3.16：需要通过内核参数 `net.ipv4.tcp_fastopen` 进行设定，此参数是一个 bitmap，`0x1` 代表客户端允许启用，`0x2` 代表服务器允许启用；默认值为 `0x1`，如果服务器要启用
    TFO，请把此内核参数值设为 `0x3`。
  * \~~Windows 10 (1607)~~（实现不正确）
  * Mac OS 10.11 / iOS 9（需要测试）
  * FreeBSD 10.3 (Server) / 12.0 (Client)：需要把内核参数 `net.inet.tcp.fastopen.server_enabled`
    以及 `net.inet.tcp.fastopen.client_enabled` 设为 `1`。（需要测试）

* 对于 Inbound，此处所设定的`正整数`代表 [待处理的 TFO 连接请求数上限](https://tools.ietf.org/html/rfc7413#section-5.1) ，**注意并非所有操作系统都支持在此设定**：
  * Linux / FreeBSD：此处的设定的`正整数`值代表上限，可接受的最大值为 2147483647，为 `true` 时将取 `256`；注意在 Linux，`net.core.somaxconn`
    会限制此值的上限，如果超过了 `somaxconn`，请同时提高 `somaxconn`。
  * Mac OS：此处为 `true` 或`正整数`时，仅代表启用 TFO，上限需要通过内核参数 `net.inet.tcp.fastopen_backlog` 单独设定。
  * Windows：此处为 `true` 或`正整数`时，仅代表启用 TFO。

* 对于 Outbound，设定为 `true` 或`正整数`在任何操作系统都仅表示启用 TFO。

> `tproxy`: "redirect" | "tproxy" | "off"

是否开启透明代理（仅适用于 Linux）。

* `"redirect"`：使用 Redirect 模式的透明代理。支持所有基于 IPv4/6 的 TCP 连接。
* `"tproxy"`：使用 TProxy 模式的透明代理。支持所有基于 IPv4/6 的 TCP 和 UDP 连接。
* `"off"`：关闭透明代理。

透明代理需要 Root 或 `CAP_NET_ADMIN` 权限。

::: danger
当 [Dokodemo-door](./inbounds/tunnel.md) 中指定了 `followRedirect`为`true`，且 Sockopt 设置中的`tproxy` 为空时，Sockopt
设置中的`tproxy` 的值会被设为 `"redirect"`。
:::

> `domainStrategy`: "AsIs"
> "UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4"
> "ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"

默认值 `"AsIs"`。

当目标地址为域名时，配置相应的值，Outbound 连接远端服务器的行为模式如下：

* 当使用 `"AsIs"` 时, Xray 不对域名进行特殊处理，到最后 Xray 将直接使用 go 自带的 Dial 发起连接，优先级固定为 RFC6724 的默认值(不会遵守 gai.conf 等配置) 通常来说为 IPv6 优先。
* 当填写其他值时，将使用 Xray-core [内置 DNS 服务器](dns.md) 服务器进行解析。若不存在DNSObject，则使用系统DNS。若有多个符合条件的IP地址时，核心会随机选择一个IP作为目标IP。
* `"IPv4"` 代表尝试仅使用 IPv4 进行连接，`"IPv4v6"` 代表尝试使用 IPv4 或 IPv6 连接，但对于双栈域名，使用 IPv4。（v4v6 调换后同理，不再赘述）
* 当在内置DNS设置了 `"queryStrategy"` 后，实际行为将会与这个选项取并，只有都被包含的IP类型才会被解析，如 `"queryStrategy": "UseIPv4"` `"domainStrategy": "UseIP"`，实际上等同于 `"domainStrategy": "UseIPv4"`。
* 当使用 `"Use"` 开头的选项时，若解析结果不符合要求（如，域名只有IPv4解析结果但使用了UseIPv6），则会回落回AsIs。
* 当使用 `"Force"` 开头的选项时，若解析结果不符合要求，则该连接会无法建立。

::: tip TIP
当使用 `"UseIP"`、`"ForceIP"` 模式时，并且 [出站连接配置](outbound.md#outboundobject) 中指定了 `sendThrough` 时，核心会根据 `sendThrough` 的值自动判断所需的 IP 类型，IPv4 或 IPv6。若手动指定了单种IP类型（如UseIPv4），但与 `sendThrough` 指定的本地地址不匹配，将会导致连接失败。
:::

::: danger

启用了此功能后，不当的配置可能会导致死循环。

一句话版本：连接到服务器，需要等待 DNS 查询结果；完成 DNS 查询，需要连接到服务器。

> Tony: 先有鸡还是先有蛋?

详细解释：

1. 触发条件：代理服务器（proxy.com）。内置 DNS 服务器，非 Local 模式。
2. Xray 尝试向 proxy.com 建立 TCP 连接 **前** ，通过内置 DNS 服务器查询 proxy.com。
3. 内置 DNS 服务器向 dns.com 建立连接，并发送查询，以获取 proxy.com 的 IP。
4. **不当的** 的路由规则，导致 proxy.com 代理了步骤 3 中发出的查询。
5. Xray 尝试向 proxy.com 建立另一个 TCP 连接。
6. 在建立连接前，通过内置 DNS 服务器查询 proxy.com。
7. 内置 DNS 服务器复用步骤 3 中的连接，发出查询。
8. 问题出现。步骤 3 中连接的建立，需要等待步骤 7 中的查询结果；步骤 7 完成查询，需要等待步骤 3 中的连接完全建立。
9. Good Game！

解决方案：

* 改内置 DNS 服务器的分流。
* 用 Hosts。
* \~~如果你还是不知道解决方案，就别用这个功能了。~~

因此，**不建议** 经验不足的用户擅自使用此功能。
:::

> `dialerProxy`: ""

一个出站代理的标识。当值不为空时，将使用指定的 outbound 发出连接。 此选项可用于支持底层传输方式的链式转发。

::: danger
此选项与 ProxySettingsObject.Tag 不兼容
:::

> `acceptProxyProtocol`: true | false

仅用于 inbound，指示是否接收 PROXY protocol。

[PROXY protocol](https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt) 专用于传递请求的真实来源 IP 和端口，**若你不了解它，请先忽略该项**。

常见的反代软件（如 HAProxy、Nginx）都可以配置发送它，VLESS fallbacks xver 也可以发送它。

填写 `true` 时，最底层 TCP 连接建立后，请求方必须先发送 PROXY protocol v1 或 v2，否则连接会被关闭。

> `tcpKeepAliveIdle`: number

TCP 空闲时间阈值，单位为秒。当 TCP 连接空闲时间达到这个阈值时，将开始发送 Keep-Alive 探测包。

对于出站, xray 使用 Chrome 的默认值 idle 与 interval 均为 45s, 该选项与 `tcpKeepAliveInterval` 任意一个设置为负数将禁用该默认 keepalive, 正数则会覆盖该默认值。

对于入站, Keep-Alive 默认禁用，该选项与 `tcpKeepAliveInterval` 任意一个非零时启用，如果只设置二者之一那么另一个将跟随操作系统设置。

> `tcpKeepAliveInterval`: number

TCP 进入 Keep-Alive 状态后发送 Keep-Alive 数据包间的时间间隔，单位为秒。其他行为见上。

> `tcpUserTimeout`: number

单位为毫秒。详细介绍：https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md

> `tcpcongestion`: ""

TCP 拥塞控制算法。仅支持 Linux。
不配置此项表示使用系统默认值。

::: tip 常见的算法

* bbr（推荐）
* cubic
* reno

:::

::: tip
执行命令 `sysctl net.ipv4.tcp_congestion_control` 获取系统默认值。
:::

> `interface`: ""

指定绑定出口网卡名称，支持 linux / iOS / Mac OS / Windows。

> `V6Only`: true | false

填写 `true` 时，监听 `::` 地址仅接受 IPv6 连接。仅支持 Linux。

> `tcpWindowClamp`: number

绑定通告的 windows 大小为该值。内核会在它与 SOCK\_MIN\_RCVBUF/2 之间选一个最大值。

> `tcpMptcp`: true | false

默认值 `false`，填写 `true` 时，启用 [Multipath TCP](https://en.wikipedia.org/wiki/Multipath_TCP)，仅客户端参数，因为 golang 在 1.24+ 版本已默认在监听时启用 MPTCP.
当前仅支持Linux，需要Linux Kernel 5.6及以上。

> `tcpNoDelay`: true | false

该选项已被删除，因为 golang 默认启用 TCP no delay。 相反地，如果想要禁用，请通过使用 customSockopt 禁用。

> `addressPortStrategy`: "none" | "SrvPortOnly" | "SrvAddressOnly" | "SrvPortAndAddress" | "TxtPortOnly" | "TxtAddressOnly" | "TxtPortAndAddress"

使用 SRV 记录或 TXT 记录指定出站使用的目标地址/端口，默认 `none` 即关闭

查询直接通过系统DNS而不是Xray的内置DNS, 尝试去查询的域名将会是出站中的域名。如果查询失败请求会按原地址和端口发出

`Srv` 开头代表查询 SRV 记录(标准格式), `Txt` 开头代表查询 TXT 记录(格式形如 `127.0.0.1:80`)

`PortOnly` 仅重置端口 `AddressOnly` 仅重置地址 `PortAndAddress` 则重置地址和端口

该选项生效在 sockopt 里的 domainStrategy 解析之前，地址重置后仍会按 domainStrategy 的规则进行解析(如果有), 但是在 Freedom 的 domainStrategy 之后，如果在其中设置了解析为 IP 则本选项无法生效。

PS: 如果有正常上网的域名流量被 AsIs 的 freedom 出站送过来，那么在此设置后会尝试解析并重置地址和端口，比如核心会尝试查询 google.com 的 SRV 记录并按记录重置目标。

> `customSockopt`: \[]

一个数组，用于高级用户指定需要的任何 sockopt, 理论上上述所有与连接有关的设置均可以在此等价设置, 自然也可以设置存在但是核心未添加的其他选项。目前支持 Linux Winows Darwin 操作系统。下方示例等价于核心中的 `"tcpcongestion": "bbr"`

使用前请确保你了解 Socket 编程。

```json
"customSockopt": [
  {
    "system": "linux",
    "type": "str",
    "level":"6",
    "opt": "13",
    "value": "bbr"
  }
]
```

> `system`: ""

可选，指定生效的系统，如果运行的系统不匹配则跳过该 sockopt. 目前可选 `linux` `windows` `darwin` (全部小写). 若留空则直接执行

> `type`: ""

必填，设置的类型，目前可选int或str.

> `level`: ""

可选，协议级别，用于指定生效范围，默认为6, 即TCP.

> `opt`: ""

操作的选项名称，使用十进制(此处示例为 TCP\_CONGESTION 的值 定义为 0xd 转换为10进制即为13)

> `value`: ""

要设置的选项值，此处示例为设置为bbr.

当 type 指定为 int 时需要使用十进制数字。

> `happyEyeballs`: [HappyEyeballsObject](#happyeyeballsobject)

RFC-8305 实现的 happyEyeballs，仅适用于 TCP。当目标为域名时对它们竞速并选择第一个成功的返回，仅当 `Sockopt.domainStrategy` 被设置为非 `AsIs` 时生效。

注意：`UseIPv4v6` / `ForceIPv4v6` 会使可用的 IP 列表被缩减到仅剩 IPv4，仅查询失败时才会回退查询 IPv6。不推荐这么用。建议使用 UseIP / ForceIP 配合 `HappyEyeballs.interleave`。

::: warning
使用这个功能时不要使用 `Freedom` 出站的 `domainStrategy`, 这会导致 `Sockopt` 只能看到被替换完毕的 IP.
:::

#### HappyEyeballsObject

```json
"happyEyeballs": {
    "tryDelayMs": 250,
    "prioritizeIPv6": false,
    "interleave": 1,
    "maxConcurrentTry": 4
}
```

> `tryDelayMs`: number

每个竞速请求发起时间的间隔，单位毫秒，默认为0(代表禁用该功能)，推荐值为 250.

> `prioritizeIPv6`: bool

排序 IP 时首个 IP 的类型，默认为 false (即 IPv4 会被排在第一个)

> `interleave`: number

RFC-8305 中的 "First Address Family count", 默认值为 1. 它定义了对不同IP版本进行排序时的交错行为。

比如等待 dial 的 IP 队列会被排序为 46464646 (设置为1) 44664466 (设置为2) (6 代表 IPv6 地址, 4 代表 IPv4 地址).

> `maxConcurrentTry`: number

最大并发数量，用于防止解析出的IP过多且均未成功时候核心也对这些IP产生大量连接。默认为4, 设置为0代表禁用 happyEyeballs.

### FinalMaskObject

FinalMask 在核心处理完包括 TLS/REALITY 在内的传输层加密后，对流量进行最后一层伪装。

```json
{
  "tcp": [
    {
      "type": "",
      "settings": {}
    }
  ],
  "udp": [
    {
      "type": "",
      "settings": {}
    }
  ],
  "quicParams": {
    "congestion": "force-brutal",
    "debug": false,
    "brutalUp": "60 mbps",
    "brutalDown": 0,
    "udpHop": {
      "ports": "20000-50000",
      "interval": "5-10"
    },
    "initStreamReceiveWindow": 8388608,
    "maxStreamReceiveWindow": 8388608,
    "initConnectionReceiveWindow": 20971520,
    "maxConnectionReceiveWindow": 20971520,
    "maxIdleTimeout": 30,
    "keepAlivePeriod": 0,
    "disablePathMTUDiscovery": false,
    "maxIncomingStreams": 1024
  }
}
```

> `tcp[n].type`: header-custom | fragment | sudoku

数组第一个为最外层伪装。

用于搭配 raw | httpupgarde | websocket | grpc | xhttp 传输层。

`header-custom`:

`fragment`:

`sudoku`:

> `tcp[n].settings`: header-custom | fragment | sudoku

#### header-custom

```json
{
  "clients": [
    [
      {
        "delay": 0,
        "rand": 0,
        "randRange": "0-255",
        "type": "",
        "packet": []
      }
    ]
  ],
  "servers": [
    [
      {
        "delay": 0,
        "rand": 0,
        "randRange": "0-255",
        "type": "",
        "packet": []
      }
    ]
  ],
  "errors": [
    [
      {
        "delay": 0,
        "rand": 0,
        "randRange": "0-255",
        "type": "",
        "packet": []
      }
    ]
  ]
}
```

`clients[n][m].delay`: 单位毫秒，为 0 则于前面的粘包发送。

`clients[n][m].rand`: 添加指定长度随机字节，与 `packet` 冲突。

`clients[n][m].randRange`: 随机字节范围，默认 0-255。

`clients[n][m].type`: `packet` 类型，`array | str | hex | base64`，默认为 array。

`clients[n][m].packet`: 添加固定数据，与 `rand` 冲突。

#### fragment

```json
{
  "packets": "tlshello",
  "length": "100-200",
  "delay": "10-20",
  "maxSplit": "3-6"
}
```

#### sudoku

```json
{
  "password": "",
  "ascii": "",

  "customTable": "", // 官方文档字段名为 custom_table
  "customTables": [""], // 官方文档字段名为 custom_tables

  "paddingMin": 0, // 官方文档字段名为 padding_min
  "paddingMax": 0 // 官方文档字段名为 padding_max
}
```

含义见其 [官方文档](https://github.com/SUDOKU-ASCII/sudoku/blob/main/configs/README.zh_CN.md) 文档字段

> `udp[n].type`: header-custom | header-dns | header-dtls | header-srtp | header-utp | header-wechat | header-wireguard | mkcp-original | mkcp-aes128gcm | noise | salamander | sudoku | xdns | xicmp

数组第一个为最外层伪装。

用于搭配 raw udp | kcp | hysteria | xhttp h3 传输层。

`header-custom`: 总是合包到数据包头。

`header-dns`: 原 mKCP 的 DNS 伪装。某些校园网在未登录的情况下允许 DNS 查询，给 KCP 添加 DNS 头。

`header-dtls`: 原 mKCP 的 DTLS 伪装。伪装成 DTLS 1.2 数据包。无额外配置。

`header-srtp`: 原 mKCP 的 SRTP 伪装。伪装成 SRTP 数据包，会被识别为视频通话数据（如 FaceTime）。无额外配置。

`header-utp`: 原 mKCP 的 uTP 伪装。伪装成 uTP 数据包，会被识别为 BT 下载数据。无额外配置。

`header-wechat`: 原 mKCP 的 WeChat Video 伪装。伪装成微信视频通话的数据包。无额外配置。

`header-wireguard`: 原 mKCP 的 WireGuard 伪装。伪装成 WireGuard 数据包。（并不是真正的 WireGuard 协议）无额外配置。

`mkcp-original`: mKCP 曾经默认应用的简单混淆，你可能需要配置它来连接以前的 mKCP 服务器。无额外配置。

`mkcp-aes128gcm`: 对应原 mKCP 的 `seed` 功能。使用 AES-128-GCM 进行混淆。

`noise`: 在发送数据前发送的噪声。

`salamander`: Salamander 混淆。（来自 Hysteria2）

`sudoku`:

`xdns`: 利用 DNS 查询来传输数据（类似 DNSTT）。它将执行标准的 DNS TXT 查询来传输载荷。

由于技术限制，它给出的 MTU 非常小，无法使用 QUIC，建议搭配 mKCP 使用。推荐的 MTU 值：客户端 130，服务端 900。

因为执行的查询是标准的，它可以透过任何 UDP DNS 服务器进行转发，尽管效率可能十分不理想。

要使用这个功能，需要服务端监听 53 端口，然后代理协议将目标指向一个 DNS 服务器（如 8.8.8.8:53），并且你拥有 `domain` 的域名，然后将其 NS 记录指向服务端。

比如持有 example.com，那么设置 a.example.com A记录 指向 ip，设置 t.example.com NS记录 指向 t.example.com，最后使用的是 t.example.com。设置 A记录 的不能为 NS记录 的子域。

`xicmp`: 要求至少 `CAP_NET_RAW` 权限且在最外层，也就是数组第一个，不可搭配 `udpHop` 与 `dialerProxy`。

> `udp[n].settings`: header-custom | header-dns | mkcp-aes128gcm | noise | salamander | sudoku | xdns | xicmp

#### header-custom

```json
{
  "client": [
    {
      "rand": 0,
      "randRange": "0-255",
      "type": "",
      "packet": []
    }
  ],
  "server": [
    {
      "rand": 0,
      "randRange": "0-255",
      "type": "",
      "packet": []
    }
  ]
}
```

`client[n].rand`: 添加指定长度随机字节，与 `packet` 冲突。

`client[n].randRange`: 随机字节范围，默认 0-255。

`client[n].type`: `packet` 类型，`array | str | hex | base64`，默认为 array。

`client[n].packet`: 添加固定数据，与 `rand` 冲突。

#### header-dns

```json
{
  "domain": "www.example.com"
}
```

#### mkcp-aes128gcm

```json
{
  "password": "your-password"
}
```

#### noise

```json
{
  "reset": 0,
  "noise": [
    {
      "rand": "1-8192",
      "randRange": "0-255",
      "type": "",
      "packet": [],
      "delay": "10-20"
    }
  ]
}
```

`noise[n].rand`: 添加随机或指定长度随机字节，与 `packet` 冲突。

`noise[n].randRange`: 随机字节范围，默认 0-255。

`noise[n].type`: `packet` 类型，`array | str | hex | base64`，默认为 array。

`noise[n].packet`: 添加固定数据，与 `rand` 冲突

`noise[n].delay`: 单位毫秒，发送噪声后延迟指定时间后再发下一个。

#### salamander

```json
{
  "password": "your-password"
}
```

#### sudoku

```json
{
  "password": "",
  "ascii": "",

  "customTable": "",
  "customTables": [""],

  "paddingMin": 0,
  "paddingMax": 0
}
```

同 TCP 版本

#### xdns

```json
{
  "domain": "www.example.com"
}
```

#### xicmp

```json
{
  "listenIp": "0.0.0.0",
  "id": 0
}
```

`listenIp`: 监听的 ip。

`id`: 如果同 ip 下有多客户端，建议服务端保持为 0。

> `quicParams`: [quicParamsObject](#quicParams)

#### quicParams

```json
{
  "congestion": "force-brutal",
  "debug": false,
  "brutalUp": "60 mbps",
  "brutalDown": 0,
  "udpHop": {
    "ports": "20000-50000",
    "interval": "5-10"
  },
  "initStreamReceiveWindow": 8388608,
  "maxStreamReceiveWindow": 8388608,
  "initConnectionReceiveWindow": 20971520,
  "maxConnectionReceiveWindow": 20971520,
  "maxIdleTimeout": 30,
  "keepAlivePeriod": 0,
  "disablePathMTUDiscovery": false,
  "maxIncomingStreams": 1024
}
```

用于 XHTTP H3 以及 hysteria 的 QUIC 配置调整。其中 XHTTP

> `congestion`: reno | bbr | brutal | force-brutal

拥塞控制算法，Hysteria 默认为 `brutal`，XHTTP H3 默认使用 `bbr`。

`reno`/`bbr`: 知名算法。

`brutal`: 与对端协商固定发包速率或降级到 BBR，只支持 Hysteria 传输（因为 XHTTP 无协商机制）。

`force-brutal`: 同 `brutal`，但强制使上行使用 `brutalUp` 固定发包速率，无视对端协商。

> `debug`: false | true

启用 bbr/brutal congestion control 日志。

> `brutalUp`: string

> `brutalDown`: string

限制的上传/下载速率。默认值为 0。

格式用户友好，支持各种常见的比特每秒写法，包括 `1000000` `100kb` `20 mb` `100 mbps` `1g` `1 tbps` 等等等，大小写不敏感，单位之间可以带或者不带空格，无单位时默认为 bps（比特每秒），不能低于 65535 bps。

协商行为和 Hysteria brutal 一致：

服务端的值将限制客户端可以选择的最大 Brutal 模式速率，为 0 表示不限制客户端。

客户端为 0 则表示使用 BBR 模式，不为 0 则表示使用 Brutal 模式，会受到服务端的限制。

注意相对论，服务端的上传是客户端的下载，服务端的下载是客户端的上传。

> `udpHop`: {"ports": string, "interval": number}

UDP 端口跳跃配置。

ports 为跳跃的端口范围，可以是一个数值类型的字符串，如 `"1234"`；或者一个数值范围，如 `"1145-1919"` 表示端口 1145 到端口 1919，这 775 个端口。可以使用逗号进行分段，如 `11,13,15-17` 表示端口 11、端口 13、端口 15 到端口 17 这 5 个端口。

interval 为端口跳跃间隔，单位为秒，至少为 5，默认 30 秒。

> `initStreamReceiveWindow`: number

> `maxStreamReceiveWindow`: number

> `initConnectionReceiveWindow`: number

> `maxConnectionReceiveWindow`: number

这四个为具体的 QUIC 窗口参数，**除非你完全明白自己在做什么，否则不建议修改这些值**。如果要改，建议保持流接收窗口与连接接收窗口的比例为 2:5

> `maxIdleTimeout`: number

最长空闲超时时间（秒）。服务器会在多长时间没有收到任何客户端数据后关闭连接，范围为 4~120 秒，默认为 30 秒。

> `keepAlivePeriod`: number

QUIC KeepAlive 间隔（秒）。范围为 2~60 秒。默认禁用。

> `disablePathMTUDiscovery`: bool

是否禁用路径 MTU 发现。

其他实现里对于 !linux && !windows && !darwin OS 为强制禁用，xray 里则非强制，如果你为非 (linux || windows || darwin) 可能需要手动禁用。

> `maxIncomingStreams`: number

服务端参数，如果设置则不得小于 8

---

---
url: /config/metrics.md
---
# Metrics

更直接（希望更好）的统计导出方式。

## MetricsObject

`MetricsObject` 对应配置文件的 `metrics` 项。

```json
{
  "metrics": {
    "tag": "Metrics",
    "listen": "127.0.0.1:11111"
  }
}
```

> `tag`: string

metrics 对应的出站代理 tag, 通过设置任意门入站+路由将任意门指向此出站即可通过该任意门访问。

> `listen`: string

更简单的方法，直接监听一个地址端口提供服务。

设置该字段时若 tag 为空会自动设置为 `Metrics`, 如果二者均未设置核心会启动失败。

## 使用方法

### pprof

访问 `http://127.0.0.1:11111/debug/pprof/` 或者使用 go tool pprof 进行调试。

反馈内存占用过多/内存泄露问题需要提供 `/debug/pprof/heap` 和 `/debug/pprof/goroutine` 的文件

### expvars

访问 `http://127.0.0.1:11111/debug/vars`

包含的变量:

* `stats` 包括所有的 inbound outbound user 数据
* `observatory` 包含了 observatory 观测结果

例如在 [luci-app-xray](https://github.com/yichya/luci-app-xray) 你可以得到这样的输出 (省略了 cmdline 和 memstats 等标准expvar内容)

```json
{
  "observatory": {
    "tcp_outbound": {
      "alive": true,
      "delay": 782,
      "outbound_tag": "tcp_outbound",
      "last_seen_time": 1648477189,
      "last_try_time": 1648477189
    },
    "udp_outbound": {
      "alive": true,
      "delay": 779,
      "outbound_tag": "udp_outbound",
      "last_seen_time": 1648477191,
      "last_try_time": 1648477191
    }
  },
  "stats": {
    "inbound": {
      "api": {
        "downlink": 0,
        "uplink": 0
      },
      "dns_server_inbound_5300": {
        "downlink": 14286,
        "uplink": 5857
      },
      "http_inbound": {
        "downlink": 74460,
        "uplink": 10231
      },
      "https_inbound": {
        "downlink": 0,
        "uplink": 0
      },
      "metrics": {
        "downlink": 6327,
        "uplink": 1347
      },
      "socks_inbound": {
        "downlink": 19925615,
        "uplink": 5512
      },
      "tproxy_tcp_inbound": {
        "downlink": 4739161,
        "uplink": 1568869
      },
      "tproxy_udp_inbound": {
        "downlink": 0,
        "uplink": 2608142
      }
    },
    "outbound": {
      "blackhole_outbound": {
        "downlink": 0,
        "uplink": 0
      },
      "direct": {
        "downlink": 97714548,
        "uplink": 3234617
      },
      "dns_server_outbound": {
        "downlink": 7116,
        "uplink": 2229
      },
      "manual_tproxy_outbound_tcp_1": {
        "downlink": 0,
        "uplink": 0
      },
      "manual_tproxy_outbound_udp_1": {
        "downlink": 0,
        "uplink": 0
      },
      "tcp_outbound": {
        "downlink": 23873238,
        "uplink": 1049595
      },
      "udp_outbound": {
        "downlink": 639282,
        "uplink": 74634
      }
    },
    "user": {}
  }
}
```

为了得到更好的可视化输出, 可以使用 [Netdata](https://github.com/netdata/netdata) (with python.d plugin):

1. 编辑相关配置文件 (`sudo /etc/netdata/edit-config python.d/go_expvar.conf`)
2. 使用下面这样的实力配置:

```
xray:
  name: 'xray'
  update_every: 2
  url: 'http://127.0.0.1:11111/debug/vars'
  collect_memstats: false
  extra_charts:
     - id: 'inbounds'
       options:
         name: 'inbounds'
         title: 'Xray System Inbounds'
         units: bytes
         family: xray
         context: xray.inbounds
         chart_type: line
       lines:
         - expvar_key: stats.inbound.tproxy_tcp_inbound.downlink
           id: 'tcp.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.tproxy_udp_inbound.downlink
           id: 'udp.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.http_inbound.downlink
           id: 'http.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.https_inbound.downlink
           id: 'https.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.socks_inbound.downlink
           id: 'socks.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.tproxy_tcp_inbound.uplink
           id: 'tcp.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.tproxy_udp_inbound.uplink
           id: 'udp.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.http_inbound.uplink
           id: 'http.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.https_inbound.uplink
           id: 'https.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.inbound.socks_inbound.uplink
           id: 'socks.uplink'
           algorithm: incremental
           expvar_type: int
     - id: 'outbounds'
       options:
         name: 'outbounds'
         title: 'Xray System Outbounds'
         units: bytes
         family: xray
         context: xray.outbounds
         chart_type: line
       lines:
         - expvar_key: stats.outbound.tcp_outbound.downlink
           id: 'tcp.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.outbound.udp_outbound.downlink
           id: 'udp.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.outbound.direct.downlink
           id: 'direct.downlink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.outbound.tcp_outbound.uplink
           id: 'tcp.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.outbound.udp_outbound.uplink
           id: 'udp.uplink'
           algorithm: incremental
           expvar_type: int
         - expvar_key: stats.outbound.direct.uplink
           id: 'direct.uplink'
           algorithm: incremental
           expvar_type: int
     - id: 'observatory'
       options:
         name: 'observatory'
         title: 'Xray Observatory Metrics'
         units: milliseconds
         family: xray
         context: xray.observatory
         chart_type: line
       lines:
         - expvar_key: observatory.tcp_outbound.delay
           id: tcp
           expvar_type: int
         - expvar_key: observatory.udp_outbound.delay
           id: udp
           expvar_type: int
```

你可以得到类似这样的结果:

![160428235-2988bf69-5d6c-41ec-8267-1bd512508aa8](https://github.com/chika0801/Xray-docs-next/assets/88967758/455e88ce-ced2-4593-a9fa-425bb293215b)

---

---
url: /config/observatory.md
---
# 连接观测

连接观测组件使用 HTTPing 的方式探测出站代理的连接状态。观测结果可以被其他组件使用，如负载均衡器。目前有 [observatory](#observatoryobject) （后台连接观测）和 [burstObservatory](#burstobservatoryobject) （突发连接观测）两种。按需选择其中之一就行。

## ObservatoryObject

`ObservatoryObject` 对应配置文件的 `observatory` 项。

```json
{
  "observatory": {
    "subjectSelector": ["outbound"],
    "probeUrl": "https://www.google.com/generate_204",
    "probeInterval": "10s",
    "enableConcurrency": false
  }
}
```

> `subjectSelector`: \[ string ]

一个字符串数组，其中每一个字符串将用于和出站代理标识的前缀匹配。在以下几个出站代理标识中：`[ "a", "ab", "c", "ba" ]`，`"subjectSelector": ["a"]` 将匹配到 `[ "a", "ab" ]`。

> `probeUrl`: string

用于探测出站代理连接状态的网址。

> `probeInterval`: string

发起探测的间隔。时间格式为数字 + 单位，比如 `"10s"`, `"2h45m"`，支持的时间单位有 `ns`, `us`, `ms`, `s`, `m`, `h`， 分别对应纳秒、微秒、毫秒、秒、分、时。

注意，由于请求间隔是固定的，隔段时间固定的请求可能会导致存在行为特征，使用带有多路复用的协议或者启用 mux 可以缓解问题。

> `enableConcurrency`: true | false

* `true` 并发探测全部匹配的出站代理，全部完成后暂停 `probeInterval` 设定的时间。
* `false` 逐个探测匹配的出站代理，每探测一个出站代理后暂停 `probeInterval` 设定的时间。

## BurstObservatoryObject

`BurstObservatoryObject` 对应配置文件的 `burstObservatory` 项。

```json
{
  "burstObservatory": {
    "subjectSelector": ["outbound"],
    "pingConfig": {}
  }
}
```

> `subjectSelector`: \[ string ]

一个字符串数组，其中每一个字符串将用于和出站代理标识的前缀匹配。在以下几个出站代理标识中：`[ "a", "ab", "c", "ba" ]`，`"subjectSelector": ["a"]` 将匹配到 `[ "a", "ab" ]`。

> `pingConfig`: [PingConfigObject](#pingconfigobject)

### PingConfigObject

```json
{
  // 针对每个出站，在 10 分钟内探测 10 次，具体的探测时间点随机
  // 如果它们全部失败，那么将在 10 ~ 20 分钟内被标记为故障节点
  // 故障后只要有一次探测成功，将被标记为健康节点，最慢需要 10 分钟
  "destination": "https://connectivitycheck.gstatic.com/generate_204",
  "connectivity": "",
  "interval": "1m",
  "sampling": 10,
  "timeout": "5s",
  "httpMethod": "HEAD"
}
```

> `destination`: string

用于探测出站代理连接状态的网址。这个网址应该返回 HTTP 204 成功状态码。
默认值 `"https://connectivitycheck.gstatic.com/generate_204"`。

> `connectivity`: string

用于检测本地网络连通性的网址。这个网址应该返回 HTTP 204 成功状态码。

空字符串表示不检测本地网络连通性。

仅当 `destination` 探测失败时会执行此探测。这样在日志中能更清晰的体现出网络不通的原因。

注意：在透明代理模式下，此请求可能会被透明代理捕获再次进入 xray 进行路由（根据你的配置方式不同）。需要采用额外手段确保它不会被透明代理捕获，比如针对网址 IP 的绕过，又或者使用 cgroup, pid 路由等方式完全使 xray 的请求不会被捕获。或者你也可以挑选一个匹配直连规则的网址，放任此请求被透明代理捕获。

> `interval`: string

针对每个出站代理，预期的**平均**每次探测间隔。

时间格式为数字 + 单位，比如 `"10s"`, `"2h45m"`，支持的时间单位有 `ns`, `us`, `ms`, `s`, `m`, `h`， 分别对应纳秒、微秒、毫秒、秒、分、时。
默认值 `"1m"`。最小允许值为 `"10s"`。如果指定的值更小，将使用 `"10s"`。

> `sampling`: number

保留最近探测结果的数量。
默认值 `10`。

> `timeout`: string

探测超时时间。格式和上面的 `interval` 相同。
默认值 `"5s"`。

> `httpMethod`: string

用于探测的 HTTP 方法（例如 `"HEAD"`、`"GET"`）。
默认值 `"HEAD"`。

::: tip
突发连接观测的工作原理是每间隔 `interval` \* `sampling`（下称探测周期），立即针对每个匹配到的出站分别调度探测任务，但是在每个任务的周期内随机时间执行探测，这意味着相较于 `observatory`（后台连接观测），本探测器的特征更不明显。但如果 interval 设置的过小，或者 sampling 过大导致频繁探测，那么特征会更明显。

`interval` 和 `sampling` 共同影响着故障转移和恢复的灵敏度。当一个节点持续探测失败，最快需要 1 个探测周期才能将节点标记为故障，最慢需要 2 个探测周期。从故障中恢复需要一次成功的探测，这取决于探测密度，最慢需要 1 个探测周期。
:::

---

---
url: /config/geodata.md
---
# 地理数据文件

用于按计划热重载地理数据文件，也可以在重载前下载新的 `.dat` 文件。适合不方便重启 Xray、又需要定期更新地理数据文件的场景。

低内存设备慎用。

## GeodataObject

`GeodataObject` 对应配置文件的 `geodata` 项。

```json
{
  "geodata": {
    "cron": "0 4 * * *",
    "outbound": "proxy",
    "assets": [
      { "url": "https://example.com/geoip.dat", "file": "geoip.dat" },
      { "url": "https://example.com/geosite.dat", "file": "geosite.dat" }
    ]
  }
}
```

> `cron`: string

标准 5 段 cron 表达式，按 Xray 运行环境的本地时区执行。例如：

* `"0 4 * * *"`：每天 04:00 执行。
* `"30 3 * * 1"`：每周一 03:30 执行。

未设置时不会启用定时任务。如果上一次任务还没有结束，下一次触发会被跳过。

> `outbound`: string

下载 geodata 文件时使用的出站代理 `tag`。不指定的话走路由模块。

> `assets`: \[ [AssetObject](#assetobject) ]

需要下载并替换的 geodata 文件列表。

如果下载后的重载失败，本次下载替换的文件会整体回滚。

### AssetObject

```json
{
  "url": "https://example.com/geoip.dat",
  "file": "geoip.dat"
}
```

> `url`: string

资源文件的下载地址，必须是 HTTPS URL。

> `file`: string

写入的资源文件名，例如 `geoip.dat`、`geosite.dat`。

该文件会按 [资源文件路径](./features/env.md#资源文件路径) 解析，并且必须是资源目录内已经存在的普通文件；不支持绝对路径或跳出资源目录的路径。

---

---
url: /config/inbounds.md
---
# Xray 入站协议列表

Xray 支持以下入站协议：

* [Tunnel](tunnel.md)
* [HTTP](http.md)
* [Shadowsocks](shadowsocks.md)
* [Socks](socks.md)
* [Trojan](trojan.md)
* [VLESS (XTLS Vision Seed)](vless.md)
* [VMess](vmess.md)
* [WireGuard](wireguard.md)
* [Hysteria](hysteria.md)
* [TUN](tun.md)

---

---
url: /config/inbounds/tunnel.md
---
# Tunnel（dokodemo-door）

Tunnel（隧道），旧称 dokodemo-door（任意门），可以监听数个本地端口，并把所有收到的数据通过 outbound 发送至指定服务器的某个端口，从而达到端口映射的效果。

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "tunnel",
      "settings": {
        // [!code focus:10]
        "allowedNetwork": "tcp",
        "rewriteAddress": "8.8.8.8",
        "rewritePort": 53,
        "portMap": {
          "5555": "1.1.1.1:7777",
          "5556": ":8888", // overrides port only
          "5557": "example.com:" // overrides address only
        },
        "followRedirect": false,
        "userLevel": 0
      }
    }
  ]
}
```

> `allowedNetwork`: "tcp" | "udp" | "tcp,udp"

可接收的网络协议类型。比如当指定为 `"tcp"` 时，仅会接收 TCP 流量。默认值为 `"tcp"`。

> `rewriteAddress`: address

将流量转发到此地址。可以是一个 IP 地址，形如 `"1.2.3.4"`，或者一个域名，形如 `"xray.com"`。字符串类型，默认为 `"localhost"`.

> `rewritePort`: number

将流量转发到目标地址的指定端口，范围 \[0, 65535]，数值类型。不填或者为 0 时默认为监听端口。

> `portMap`: map\[string]string

一个map, 映射本地端口和需要的远程地址/端口(如果 inbound 监听了数个端口), 如果本地端口未包含在其中则按 `rewriteAddress`/`rewritePort` 设置处理。

> `followRedirect`: true | false

当值为 `true` 时，dokodemo-door 会识别出由 iptables 转发而来的数据，并转发到相应的目标地址。

可参考 [传输配置](../transport.md#sockoptobject) 中的 `tproxy` 设置。

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值. 如不指定, 默认为 0。

## 用处

任意门主要有两个用处 一个是用作透明代理(见下)，另一个是映射一个端口。

有时一些服务并不支持使用 Socks5 这样的正向代理，使用 Tun 或者 Tproxy 又有些小题大做了，而这些服务又只和一个 IP 一个端口通信 (比如: iperf, Minecraft server, Wireguard endpoint), 就可以用到任意门。

如以下 Config (假设默认出站为一有效代理)

```json
{
  "listen": "127.0.0.1",
  "port": 25565,
  "protocol": "tunnel",
  "settings": {
    "allowedNetwork": "tcp",
    "rewriteAddress": "mc.hypixel.net",
    "rewritePort": 25565,
    "followRedirect": false,
    "userLevel": 0
  },
  "tag": "mc"
}
```

这时候核心会监听 127.0.0.1:25565 并通过默认出站转发至 mc.hypixel.net:25565 (一个MC服务器), 这时候再通过 Minecraft 客户端连接 127.0.0.1:25565, 就相当于通过代理连接了 Hypixel 服务器。

## 透明代理配置样例

此部分请参考[透明代理（TProxy）配置教程](../../document/level-2/tproxy)。

---

---
url: /config/inbounds/http.md
---
# HTTP

HTTP 协议。

::: danger
**http 协议没有对传输加密，不适宜经公网中传输，更容易成为被人用作攻击的肉鸡。**
:::

`http` 入站更有意义的用法是在局域网或本机环境下监听，为其他程序提供本地服务。

::: tip TIP 1
`http proxy` 只能代理 tcp 协议，udp 系的协议均不能通过。
:::

::: tip TIP 2
在 Linux 中使用以下环境变量即可在当前 session 使用全局 HTTP 代理（很多软件都支持这一设置，也有不支持的）。

* `export http_proxy=http://127.0.0.1:8080/` (地址须改成你配置的 HTTP 入站代理地址)
* `export https_proxy=$http_proxy`
  :::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "http",
      "settings": {
        // [!code focus:8]
        "users": [
          {
            "user": "my-username",
            "pass": "my-password"
          }
        ],
        "allowTransparent": false,
        "userLevel": 0
      }
    }
  ]
}
```

> `users`: \[[UserObject](#userobject)]

一个数组，数组中每个元素为一个用户帐号。默认值为空。

当 `users` 非空时，HTTP 代理将对入站连接进行 Basic Authentication 验证。

> `allowTransparent`: true | false

当为 `true` 时，会转发所有 HTTP 请求，而非只是代理请求。

::: tip
若配置不当，开启此选项会导致死循环。
:::

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

### UserObject

```json
{
  "user": "my-username",
  "pass": "my-password"
}
```

> `user`: string

用户名，字符串类型。必填。

> `pass`: string

密码，字符串类型。必填。

---

---
url: /config/inbounds/shadowsocks.md
---
# Shadowsocks

[Shadowsocks](https://zh.wikipedia.org/wiki/Shadowsocks) 协议，兼容大部分其它版本的实现。

目前兼容性如下：

* 支持 TCP 和 UDP 数据包转发，其中 UDP 可选择性关闭；
* 推荐的加密方式：
  * 2022-blake3-aes-128-gcm
  * 2022-blake3-aes-256-gcm
  * 2022-blake3-chacha20-poly1305
* 其他加密方式
  * aes-256-gcm
  * aes-128-gcm
  * chacha20-poly1305 或称 chacha20-ietf-poly1305
  * xchacha20-poly1305 或称 xchacha20-ietf-poly1305
  * none 或 plain

Shadowsocks 2022 新协议格式提升了性能并带有完整的重放保护，解决了旧协议的以下安全问题：

* [Shadowsocks AEAD 加密方式设计存在严重漏洞，无法保证通信内容的可靠性](https://github.com/shadowsocks/shadowsocks-org/issues/183)
* 原有 TCP 重放过滤器误报率随时间增加
* 没有 UDP 重放保护
* 可用于主动探测的 TCP 行为

::: danger
"none" 不加密方式下流量将明文传输。为确保安全性, 不要在公共网络上使用。
:::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "shadowsocks",
      "settings": {
        // [!code focus:11]
        "network": "tcp,udp",
        "method": "aes-256-gcm",
        "password": "114514",
        "level": 0,
        "email": "love@xray.com",
        "users": [
          {
            "password": "1919810",
            "method": "aes-128-gcm"
          }
        ]
      }
    }
  ]
}
```

> `network`: "tcp" | "udp" | "tcp,udp"

服务端端口**监听**的网络类型。默认值为 `"tcp"`。

注意，这只是监听，主要影响并控制 shadowsocks 的原生 UDP 传输，设置为 `"tcp"` 不代表入站会拒绝代理 UDP 的请求。UDP 代理请求仍可以被 shadowsocks 出站的 uot 或者 mux.cool 等包装到 TCP 报文中发送到服务端，不受此选项控制。

> `method`: string

加密方式，可选项见上。

> `password`: string

必填。

* Shadowsocks 2022

  使用与 WireGuard 类似的预共享密钥作为密码。

  使用 `openssl rand -base64 <长度>` 以生成与 shadowsocks-rust 兼容的密钥，长度取决于所使用的加密方法。

  | 加密方法                      | 密钥长度 |
  | ----------------------------- | -------: |
  | 2022-blake3-aes-128-gcm       |       16 |
  | 2022-blake3-aes-256-gcm       |       32 |
  | 2022-blake3-chacha20-poly1305 |       32 |

  在 Go 实现中，32 位密钥始终工作。

* 其他加密方法

  任意字符串。不限制密码长度，但短密码会更可能被破解，建议使用 16 字符或更长的密码。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。
`level` 的值, 对应 [policy](../policy.md#levelpolicyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

用户邮箱，用于区分不同用户的流量（日志、统计）。

> `users`: \[ [UserObject](#userobject) ]

一个数组，代表一组服务端认可的用户。

其中每一项是一个用户 [UserObject](#userobject)。

当存在此选项时，代表启用多用户模式。

### UserObject

```json
{
  "password": "1919810",
  "method": "aes-256-gcm",
  "level": 0,
  "email": "love@xray.com"
}
```

> `method`: string

* 当 InboundConfigurationObject 中的 `method` 不为 SS2022 选项时，可以在此为每个用户指定 `"method"`。(`"method"`中也仅支持非 SS2022 选项) 与`"password"`(与此同时 InboundConfigurationObject 中的设置的 `"password"` 将会被忽略)。

* 当 InboundConfigurationObject 中的 `method` 为 SS2022 选项时，出于安全考量，不再支持为单个用户设置 `"method"`，统一为 InboundConfigurationObject 所指定的`"method"`。

> `password`: string

注意 SS2022 并不会像旧 SS 一样忽略上层 `"password"`, 客户端的正确密码写法应为, `ServerPassword:UserPassword`。如:`"password": "114514:1919810"`

> 其余选项

与 InboundConfigurationObject 中的含义一致。

---

---
url: /config/inbounds/socks.md
---
# Socks

标准 Socks 协议实现，兼容 [Socks 4](http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol)、[Socks 4a](https://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4A.protocol) Socks 5, 以及 **HTTP**。

::: danger
**Socks 协议没有对传输加密，不适宜经公网中传输**
:::

`Socks` 入站更有意义的用法是在局域网或本机环境下监听，为其他程序提供本地服务。

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "socks",
      "settings": {
        // [!code focus:10]
        "auth": "noauth",
        "users": [
          {
            "user": "my-username",
            "pass": "my-password"
          }
        ],
        "udp": false,
        "ip": "127.0.0.1",
        "userLevel": 0
      }
    }
  ]
}
```

> `auth`: "noauth" | "password"

Socks 协议的认证方式，支持 `"noauth"` 匿名方式和 `"password"` 用户密码方式。

当使用 password 时，发往入站的HTTP请求也会要求同样的账号密码。

默认值为 `"noauth"`。

> `users`: \[ [UserObject](#userobject) ]

一个数组，数组中每个元素为一个用户帐号。

此选项仅当 `auth` 为 `password` 时有效。

默认值为空。

> `udp`: true | false

是否开启 UDP 协议的支持。

默认值为 `false`。

> `ip`: address

当开启 UDP 时，Xray 需要知道本机的 IP 地址。

“本机的 IP 地址” 含义是客户端发起UDP连接时可以拿着这个 IP 找到服务端，默认是服务器被TCP连接时的本地 IP. 大多数时候应该可以正常工作，但是在经过一些经过 NAT 的系统时可能导致工作异常需要修改这个参数为正确的公网IP.

警告，如果你的机器上存在多个IP地址，将会受到 [入站监听](../inbound.md#inboundobject) 里UDP监听 0.0.0.0 时有关的影响。

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

### UserObject

```json
{
  "user": "my-username",
  "pass": "my-password"
}
```

> `user`: string

用户名，字符串类型。必填。

> `pass`: string

密码，字符串类型。必填。

---

---
url: /config/inbounds/trojan.md
---
# Trojan

[Trojan](https://trojan-gfw.github.io/trojan/protocol) 协议

::: danger
Trojan 被设计工作在正确配置的加密 TLS 隧道
:::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "trojan",
      "settings": {
        // [!code focus:12]
        "users": [
          {
            "password": "password",
            "email": "love@xray.com",
            "level": 0
          }
        ],
        "fallbacks": [
          {
            "dest": 80
          }
        ]
      }
    }
  ]
}
```

> `users`: \[ [UserObject](#userobject) ]

一个数组，代表一组服务端认可的用户.

其中每一项是一个用户 [UserObject](#userobject)。

> `fallbacks`: \[ [FallbackObject](../features/fallback.md) ]

一个数组，包含一系列强大的回落分流配置（可选）。
fallbacks 的具体配置请点击[FallbackObject](../features/fallback.md#fallbacks-配置)

::: tip
Xray 的 Trojan 有完整的 fallbacks 支持，配置方式完全一致。
触发回落的条件也与 VLESS 类似：首包长度 < 58 或第 57 个字节不为 `\r`（因为 Trojan 没有协议版本）或身份认证失败。
:::

### UserObject

```json
{
  "password": "password",
  "email": "love@xray.com",
  "level": 0
}
```

> `password`: string

必填，任意字符串。

> `email`: string

邮件地址，可选，用于标识用户

::: danger
如果存在多个 UserObject, 请注意 email 不可以重复。
:::

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

---

---
url: /config/inbounds/vless.md
---
# VLESS（XTLS Vision Seed）

VLESS 是一个无状态的轻量传输协议，它分为入站和出站两部分，可以作为 Xray 客户端和服务器之间的桥梁。

与 [VMess](./vmess.md) 不同，VLESS 不依赖于系统时间，认证方式同样为 UUID。

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "vless",
      "settings": {
        // [!code focus:15]
        "users": [
          {
            "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
            "level": 0,
            "email": "love@xray.com",
            "flow": "xtls-rprx-vision",
            "reverse": {}
          }
        ],
        "decryption": "none",
        "fallbacks": [
          {
            "dest": 80
          }
        ]
      }
    }
  ]
}
```

> `users`: \[ [UserObject](#userobject) ]

一个数组，代表一组服务端认可的用户。

其中每一项是一个用户 [UserObject](#userobject)。

> `decryption`: "none"

[VLESS 加密](https://github.com/XTLS/Xray-core/pull/5067)设置。不能留空，禁用需显式设置为 `"none"`.

推荐大多数用户使用命令 `xray vlessenc` 自动生成该字段确保编写不会出错。下方详细配置仅推荐高级用户阅读。

::: details 详细配置
其格式为一串字段由 `.` 连接的详细配置。 如 `mlkem768x25519plus.native.600s.100-111-1111.75-0-111.50-0-3333.ptjHQxBQxTJ9MWr2cd5qWIflBSACHOevTauCQwa_71U`. 本文档将用点分开的单独部分称之为一个块

* 第1个块为握手方式，目前有且仅有 `mlkem768x25519plus`. 要求服务端与客户端一致
* 第2个块为加密方式，可选 `native`/`xorpub`/`random`, 分别对应: 原始格式数据包/原始格式+混淆公钥部分/全随机数（类似 VMESS/Shadows socks）。要求服务端与客户端一致
* 第3个块为会话恢复票据有效时间。格式为 `600s` 或 `100-500s`. 前者将在该时长和该时长的一半之间随机一个时间(如 `600s`=`300-600s`)，后者则手动指定随即范围

往后为 padding, 连接建立后服务端发送一些垃圾数据用以混淆长度特征，无需与客户端相同(出站的相同部分为客户端向服务端方向发送的 padding)，属于可变长部分，格式为 `padding.delay.padding`+`(.delay.padding)`\*n（可插入多个 padding, 要求两个 padding 块之间必须包含一个 delay 块） 比如可以写一个超长的 `padding.delay.padding.delay.padding.delay.padding.delay.padding.delay.padding`

* `padding` 格式为 `probability-min-max` 如 `100-111-1111` 含义为 100% 发送一个长度 111~1111 的padding.
* `delay` 格式同样为 `probability-min-max` 如 `75-0-111` 含义为 75% 的概率等待 0~111 毫秒

第一个 padding 块存在特殊要求，要求概率为 100% 且最小长度大于 0. 若不存在任何 padding, 核心自动使用 `100-111-1111.75-0-111.50-0-3333` 作为 padding 设置。

最后一个块会被核心识别为认证客户端使用的参数，可用 `./xray x25519`（使用 PrivateKey 部分） 或 `./xray mlkem768`（使用 Seed 部分） 生成，要求与客户端对应。`mlkem768` 属于后量子算法，可以防止（未来）客户端参数泄露后被量子计算机破解出私钥并冒充服务端。该参数仅用于验证，握手过程无论如何都是后量子安全的，现有的加密数据无法被未来出现的量子计算机破解。
:::

> `fallbacks`: \[ [FallbackObject](../features/fallback.md) ]

一个数组，包含一系列强大的回落分流配置（可选）。
fallbacks 的具体配置请点击 [FallbackObject](../features/fallback.md#fallbacks-配置)

### UserObject

```json
{
  "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
  "level": 0,
  "email": "love@xray.com",
  "flow": "xtls-rprx-vision",
  "reverse": {}
}
```

> `id`: string

VLESS 的用户 ID，可以是任意小于 30 字节的字符串, 也可以是一个合法的 UUID.
自定义字符串和其映射的 UUID 是等价的, 这意味着你将可以这样在配置文件中写 id 来标识同一用户,即

* 写 `"id": "我爱🍉老师1314"`,
* 或写 `"id": "5783a3e7-e373-51cd-8642-c83782b807c5"` (此 UUID 是 `我爱🍉老师1314` 的 UUID 映射)

其映射标准在 [VLESS UUID 映射标准：将自定义字符串映射为一个 UUIDv5](https://github.com/XTLS/Xray-core/issues/158)

你可以使用命令 `xray uuid -i "自定义字符串"` 生成自定义字符串所映射的的 UUID。

> 也可以使用命令 `xray uuid` 生成随机的 UUID.

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

用户邮箱，用于区分不同用户的流量（会体现在日志、统计中）。

> `flow`: string

流控模式，用于选择 XTLS 的算法。

目前入站协议中有以下流控模式可选：

* 无 `flow` 或者 空字符： 使用普通 TLS 代理
* `xtls-rprx-vision`：使用新 XTLS 模式 包含内层握手随机填充

XTLS 仅在以下搭配下可用

* TCP+TLS/Reality 此时将直接在底层对拷加密后的数据（若传输的是 TLS 1.3）。
* VLESS Encryption 无底层传输限制，若底层不支持直接对拷（见上）则仅穿透 Encryption.

> `reverse`: struct

VLESS 极简反向代理配置。

存在此项代表来自该用户的连接可以被用作可以用于建立反向代理隧道，同时禁用普通的正向代理用途。

```json
"reverse": {
  "tag": "r-outbound"
}
```

`tag` 为该反向代理的出站代理 tag. 使用路由将流量路由到该出站将会透过反向代理转发到连入的客户端路由系统中(客户端配置详见 VLESS 出站).

当有多个不同的连接(可以来自不同的设备)接入时核心会对每个请求随机选择一条派发反向代理数据。

::: tip
完整教程：[VLESS 反向代理示例](../../document/level-2/vless_reverse.md)
:::

---

---
url: /config/inbounds/vmess.md
---
# VMess

[VMess](../../development/protocols/vmess.md) 是一个加密传输协议，通常作为 Xray 客户端和服务器之间的桥梁。

::: danger
VMess 依赖于系统时间，请确保使用 Xray 的系统 UTC 时间误差在 120 秒之内，时区无关。在 Linux 系统中可以安装`ntp`服务来自动同步系统时间。
:::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "vmess",
      "settings": {
        // [!code focus:10]
        "users": [
          {
            "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
            "level": 0,
            "email": "love@xray.com"
          }
        ],
        "default": {
          "level": 0
        }
      }
    }
  ]
}
```

> `users`: \[ [UserObject](#userobject) ]

一个数组，代表一组服务端认可的用户.

其中每一项是一个用户[UserObject](#userobject)。

当此配置用作动态端口时，Xray 会自动创建用户。

> `default`: [DefaultObject](#defaultobject)

可选，`users` 的默认配置。仅在配合 `detour` 时有效。

### UserObject

```json
{
  "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
  "level": 0,
  "email": "love@xray.com"
}
```

> `id`: string

Vmess 的用户 ID，可以是任意小于 30 字节的字符串, 也可以是一个合法的 UUID.

::: tip
自定义字符串和其映射的 UUID 是等价的, 这意味着你将可以这样在配置文件中写 id 来标识同一用户,即

* 写 `"id": "我爱🍉老师1314"`,
* 或写 `"id": "5783a3e7-e373-51cd-8642-c83782b807c5"` (此 UUID 是 `我爱🍉老师1314` 的 UUID 映射)
  :::

其映射标准在 [VLESS UUID 映射标准：将自定义字符串映射为一个 UUIDv5](https://github.com/XTLS/Xray-core/issues/158)

你可以使用命令 `xray uuid -i "自定义字符串"` 生成自定义字符串所映射的的 UUID。

> 也可以使用命令 `xray uuid` 生成随机的 UUID.

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

用户邮箱地址，用于区分不同用户的流量。

### DefaultObject

```json
{
  "level": 0
}
```

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

---

---
url: /config/inbounds/wireguard.md
---
# Wireguard

User-space Wireguard 协议实现。

::: danger
**Wireguard 协议并非专门为翻墙而设计，若在最外层过墙，存在特征可能导致服务器被封锁**
:::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "wireguard",
      "settings": {
        // [!code focus:8]
        "secretKey": "PRIVATE_KEY",
        "peers": [
          {
            "publicKey": "PUBLIC_KEY",
            "allowedIPs": [""]
          }
        ],
        "mtu": 1420 // optional, default 1420
      }
    }
  ]
}
```

> `secretKey`: string

私钥。必填。

> `mtu`: int

Wireguard 底层 tun 的分片大小。

一个wireguard数据包的结构如下

```
- 20-byte IPv4 header or 40 byte IPv6 header
- 8-byte UDP header
- 4-byte type
- 4-byte key index
- 8-byte nonce
- N-byte encrypted data
- 16-byte authentication tag
```

`N-byte encrypted data`即为我们需要的MTU的值，根据endpoint是IPv4还是IPv6，具体的值可以是1440(IPv4)或者1420(IPv6)，如果处于特殊环境下再额外减掉即可(如家宽PPPoE额外-8)。

> `peers`: \[ [Peers](#peers) ]

peers 服务器列表，其中每一项是一个服务器配置。

### Peers

```json
{
  "publicKey": "PUBLIC_KEY",
  "allowedIPs": ["0.0.0.0/0"] // optional, default ["0.0.0.0/0", "::/0"]
}
```

> `publicKey`: string

公钥，用于验证

> `allowedIPs`: string array

允许的源IP

---

---
url: /config/inbounds/hysteria.md
---
# Hysteria

::: tip
`hysteria protocol` 本身无认证，`users` 仅在搭配 `hysteria` 传输层时生效
:::

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "hysteria",
      "settings": {
        // [!code focus:8]
        "version": 2,
        "users": [
          {
            "auth": "5783a3e7-e373-51cd-8642-c83782b807c5",
            "level": 0,
            "email": "love@xray.com"
          }
        ]
      }
    }
  ]
}
```

> `version`: number

Hysteria 版本，必须为 2。

> `users`: \[ [UserObject](#userobject) ]

一个数组，代表一组服务端认可的用户。

### UserObject

```json
{
  "auth": "5783a3e7-e373-51cd-8642-c83782b807c5",
  "level": 0,
  "email": "love@xray.com"
}
```

> `auth`: string

任意长度字符串。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

用户邮箱，用于区分不同用户的流量（会体现在日志、统计中）。

---

---
url: /config/inbounds/tun.md
---
# TUN

创建一个 TUN 接口，发往此接口的流量将由 Xray 处理。目前支持 Windows、Linux、macOS 和 FreeBSD。

## InboundConfigurationObject

`InboundConfigurationObject` 对应 [`InboundObject`](../inbound.md) 中的 `settings` 项。

```json
{
  "inbounds": [
    {
      // ...
      "protocol": "tun",
      "settings": {
        // [!code focus:7]
        "name": "xray0",
        "mtu": 1500,
        "gateway": ["10.0.0.1/16", "fc00::1/64"],
        "dns": ["1.1.1.1", "8.8.8.8"],
        "userLevel": 0,
        "autoSystemRoutingTable": ["0.0.0.0/0", "::/0"],
        "autoOutboundsInterface": "auto"
      }
    }
  ]
}
```

> `name`: string

创建的 TUN 接口名。默认 `"xray0"`

> `mtu`: number

接口的 MTU。默认值为 `1500`。

> `gateway`: \[string]

为 TUN 接口配置的地址前缀列表，通常分别填写 IPv4 / IPv6，例如 `"10.0.0.1/16"`、`"fc00::1/64"`。

> `dns`: \[string]

为 TUN 接口配置的 DNS 服务器列表，例如 `"1.1.1.1"`、`"8.8.8.8"`。

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值. 如不指定, 默认为 0。

> `autoSystemRoutingTable`: \[string]

自动写入系统路由表的目标网段列表。每一项均为 CIDR，例如 `"0.0.0.0/0"` 表示所有 IPv4 流量，`"::/0"` 表示所有 IPv6 流量。

> `autoOutboundsInterface`: string

自动为 Xray 的出站绑定物理网络接口，用于避免把 Xray 自己发出的流量再次送回 TUN 造成回环。

默认值为 `null`，即未配置。可填写具体接口名，也可填写 `"auto"` 让 Xray 自动选择。如果配置了 `autoSystemRoutingTable` 但未显式指定此项，Xray 会自动按 `"auto"` 处理。

## 使用提示

如果未配置 `autoSystemRoutingTable`，仍需要手动配置路由将数据导向创建的 TUN 接口，否则它只是个接口。

配置了 `gateway`、`dns`、`autoSystemRoutingTable` 和 `autoOutboundsInterface` 后，Xray 可以在支持的平台上自动完成一部分系统侧配置；如果你的平台尚未实现这些自动设置，或者需要更细粒度的策略路由，仍然需要配合系统工具手动处理。

如果只想代理某一个或一些进程，Xray 路由系统中的进程名路由会十分有用。

::: warning
注意可能的流量回环的问题，设置路由后可能将 Xray 发出的请求发回 Xray 造成回环！
优先使用 `autoOutboundsInterface` 避免此问题；如果你需要手动控制，也可以使用 `sockopt` 中的 `interface` 绑定实际的物理网络接口。`ipconfig` (Windows) `ip a` (Linux) 将有助于找到你需要的接口名。
或者使用出站的 `sendThrough` 它直接在 OutboundObject 中可用，没有 sockOpt.interface 那么深的嵌套层级，这里需要使用的是网卡上的 IP，比如 192.168.1.2 （如你所见它的缺点是不能自动支持双栈，请按你出站的实际使用的 IP 选择）。
:::

---

---
url: /config/outbounds.md
---
# Xray 出站协议列表

Xray 支持以下出站协议：

* [Blackhole](blackhole.md)
* [DNS](dns.md)
* [Freedom (fragment, noises)](freedom.md)
* [HTTP](http.md)
* [Loopback](loopback.md)
* [Shadowsocks](shadowsocks.md)
* [Socks](socks.md)
* [Trojan](trojan.md)
* [VLESS (XTLS Vision Seed)](vless.md)
* [VMess](vmess.md)
* [WireGuard](wireguard.md)
* [Hysteria](hysteria.md)

---

---
url: /config/outbounds/blackhole.md
---
# Blackhole

Blackhole（黑洞）是一个出站数据协议，它会阻碍所有数据的出站，配合 [路由配置](../routing.md) 一起使用，可以达到禁止访问某些网站的效果。

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "blackhole",
      "settings": {
        // [!code focus:3]
        "response": {
          "type": "none"
        }
      }
    }
  ]
}
```

> `response`: [ResponseObject](#responseobject)

配置黑洞的响应数据。

Blackhole 会在收到待转发数据之后，发送指定的响应数据，然后关闭连接，待转发的数据将被丢弃。
如不指定此项，Blackhole 将直接关闭连接。

### ResponseObject

```json
{
  "type": "none"
}
```

> `type`: "http" | "none"

当 `type` 为 `"none"`（默认值）时，Blackhole 将直接关闭连接。

当 `type` 为 `"http"` 时，Blackhole 会发回一个简单的 HTTP 403 数据包，然后关闭连接。

---

---
url: /config/outbounds/dns.md
---
# DNS

DNS 是一个出站协议，用于接收由 routing 送入的 DNS 查询，并按规则转发或处理。

此出站只支持传统明文 DNS，即基于 UDP 和 TCP 的查询；DoH、DoT、DoQ 等非传统明文 DNS 不适用于此出站。常见场景是 TUN、透明代理或 `dokodemo-door` 接收到 DNS 流量后，再由 routing 将其分流到此出站。

它可以按规则将查询放行到目标 DNS 服务器、`hijack` 到内置的 [DNS 服务器](../dns.md) 进一步处理、直接丢弃或显式拒绝，也可以改写目标地址、端口和传输协议。

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "dns",
      "settings": {
        // [!code focus:15]
        "rewriteNetwork": "udp",
        "rewriteAddress": "1.1.1.1",
        "rewritePort": 53,
        "userLevel": 0,
        "rules": [
          {
            "action": "reject",
            "domain": ["domain:example.com"]
          },
          {
            "action": "direct",
            "qtype": 65,
            "domain": ["geosite:geolocation-!cn"]
          }
        ]
      }
    }
  ]
}
```

上例仅示意各字段写法，完整配置见下方示例。

> `rewriteNetwork`: \[ "tcp" | "udp" ]

修改 DNS 流量的传输层协议，可选值为 `"tcp"` 和 `"udp"`。当不指定时，保持来源的传输方式不变。

> `rewriteAddress`: address

修改 DNS 服务器地址。当不指定时，保持来源中指定的地址不变。

> `rewritePort`: number

修改 DNS 服务器端口。当不指定时，保持来源中指定的端口不变。

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

`userLevel` 的值，对应 [policy](../policy.md#policyobject) 中 `level` 的值。如不指定，默认为 `0`。

> `rules`: \[[RuleObject](#ruleobject)]

按顺序匹配 DNS 查询规则，并支持按 `qtype` 和 `domain` 进行细粒度控制。

若未命中任何规则，则使用内置兜底规则：A 和 AAAA 查询会被导入内置 DNS 模块，其它类型会被显式拒绝。

### RuleObject

```json
{
  "action": "hijack",
  "qtype": 1,
  "domain": ["geosite:cn"]
}
```

规则中的各匹配条件为与关系；省略某个条件时，表示对此条件不作限制。

> `action`: \[ "direct" | "hijack" | "drop" | "reject" ]

定义规则命中后的动作。

* `direct`: 直接放行到目标 DNS 服务器；若同时配置了出站级别的 `rewriteNetwork`、`rewriteAddress` 或 `rewritePort`，则按改写后的目标继续转发。
* `hijack`: 将查询导入内置的 [DNS 服务器](../dns.md) 继续处理，可用于按照内置 DNS 的配置进一步分流；目前仅支持 A 和 AAAA 记录。
* `drop`: 直接丢弃请求，不返回响应。
* `reject`: 返回显式拒绝响应，相比 `drop` 可以避免部分应用长时间等待 DNS 超时或反复重试。

> `qtype`: number | string

匹配 DNS 查询类型，形式如下：

* 整型数值 具体的查询类型，如 `"qtype": 1` 代表 A 查询，`"qtype": 28` 代表 AAAA 查询。
* 字符串：可以是一个只有数字的字符串，如 `"qtype": "28"`；或者一个数值范围，如 `"qtype": "5-10"` 表示type 5 到 type 10，这 6 个类型。可以使用逗号进行分段，如 `11,13,15-17` 表示type 11、type 13、type 15 到 type 17 这 5 个类型。

具体数字编号可参考 [IANA 文档](https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml)。

> `domain`: \[string]

匹配域名列表，写法与 [路由规则中的 `domain`](../routing.md#ruleobject) 一致。

---

---
url: /config/outbounds/freedom.md
---
# Freedom（fragment、noises）

Freedom 是一个出站协议，可以用来向任意网络发送（正常的） TCP 或 UDP 数据。

::: warning
该出站在服务器端和反向代理端存在默认安全策略，可能会阻止一些目标，放行方式见下文 `finalRules`。
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "freedom",
      "settings": {
        // [!code focus:27]
        "domainStrategy": "AsIs",
        "redirect": "127.0.0.1:3366",
        "userLevel": 0,
        "fragment": {
          "packets": "tlshello",
          "length": "100-200",
          "interval": "10-20" // 单位ms
        },
        "noises": [
          {
            "type": "base64",
            "packet": "7nQBAAABAAAAAAAABnQtcmluZwZtc2VkZ2UDbmV0AAABAAE=",
            "delay": "10-16"
          }
        ],
        "proxyProtocol": 0,
        "finalRules": [
          {
            "action": "block",
            "network": "tcp",
            "port": "22,25,465,587"
          },
          {
            "action": "block",
            "ip": ["geoip:cn"]
          }
        ]
      }
    }
  ]
}
```

> `domainStrategy`: "AsIs"
> "UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4"
> "ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"

默认值 `"AsIs"`。

所有参数含义均约等于 [sockopt](../transport.md#sockoptobject) 中的 domainStrategy.

在这里使用 AsIs 才可以把域名交给后面的 sockopt 模块，如果在这里设置非 AsIs 导致域名被解析为具体 IP 会使后续的 sockopt.domainStrategy 以及其相关的 happyEyeballs 失效。（如果不调整这两个设置则没有负面影响）

Freedom 在发送 UDP 时出于一些原因无视 sockopt 中的 domainStrategy 并在默认状态下强制偏好 IPv4.

> `redirect`: address\_port

Freedom 会强制将所有数据发送到指定地址（而不是 inbound 指定的地址）。

其值为一个字符串，样例：`"127.0.0.1:80"`，`":1234"`。

当地址不指定时，如 `":443"`，Freedom 不会修改原先的目标地址。
当端口为 `0` 时，如 `"xray.com: 0"`，Freedom 不会修改原先的端口。

> `userLevel`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `fragment`: map

一些键值对配置项，用于控制发出的 TCP 分片，在某些情况下可以欺骗审查系统，比如绕过 SNI 黑名单。

`"length"`和`"interval"` 均为 [Int32Range](../../development/intro/guide.md#int32range) 类型

`"packets"`：支持两种分片方式 "1-3" 是 TCP 的流切片，应用于客户端第 1 至第 3 次写数据。"tlshello" 是 TLS 握手包切片。

`"length"`：分片包长 (byte)

`"interval"`：分片间隔（ms）

当其为 0 且设置 `"packets": "tlshello"` 时，被分片的 Client Hello 将会在一个TCP包中发送（如果其原始大小未超过MSS或MTU导致被系统自动分片）

> `noises`: array

UDP noise, 用于在发出UDP连接前发出一些随机数据作为“噪声”，出现该结构体则视为启用，可能可以欺骗嗅探器，也可能破坏正常连接。Use at your own risk. 出于这个原因，它会绕过53端口因为这会破坏 DNS

为一个数组，可以定义多个要发出的噪声数据包，数组中单个元素定义如下

`"type"`: 噪声数据包类型，目前支持`"rand"`(随机数据), `"str"`(用户自定义字符串), `"base64"`(base64编码过的自定义二进制数据)

`"packet"`: 基于前面的 `type` 要发送的数据包内容

* 当 `type` 为 rand 时，这里指定随机数据的长度 可以是固定值 `"100"` 或者浮动值 `"50-150"`
* 当 `type` 为 str 时，这里指定要发送的字符串
* 当 `type` 为 hex 时，这里指定以 hex 形式表示的的二进制数据
* 当 `type` 为 base64 时，这里指定 base64 过的二进制数据

`"delay"`: 延迟，单位毫秒。发送该噪声包后核心会等待该时间后再发送下一个噪声包或真实数据，默认不等待，为 [Int32Range](../../development/intro/guide.md#int32range) 类型

> `proxyProtocol`: number

PROXY protocol 通常配合 `redirect` 重定向到开启了 PROXY protocol 协议的 Nginx 或其他后端服务中。如果后端服务不支持 PROXY protocol 协议，连接将会被断开。

proxyProtocol 的值为 PROXY protocol 版本号，可选 `1` 或 `2`，如不指定，默认为 `0` 不启用。

> `finalRules`: \[[FinalRuleObject](#finalruleobject)]

按顺序匹配 Freedom 最终出站规则，用于放行或阻止连接目标。

相比在 `routing` 中封锁，`finalRules` 位于 Freedom 最终出站阶段：在解析出最终 IP 后、拨号前匹配；此外 UDP 在收发时还会逐包匹配，因此更严谨、更彻底；每条规则匹配耗时约 50~150ns。

注意：只要 Freedom 需要执行 `finalRules`，当 `domainStrategy` 为 `AsIs` 且目标为域名时，Freedom 仍会先通过操作系统 DNS 将目标解析为 IP，再执行规则匹配。此时目标已不再是域名，后续的 `sockopt.domainStrategy` 及其 `happyEyeballs` 不再生效。

::: warning
服务器端和反向代理端存在默认兜底安全策略：

若未命中任何显式规则，则使用内置兜底规则：来自 VLESS 反向代理的流量默认阻止所有目标；来自 `VLESS`、`VMess`、`Trojan`、`Shadowsocks`、`Hysteria` 或 `WireGuard` 入站的流量默认阻止私有及保留 IP；其它流量默认全部放行。

若服务器端需要允许客户端访问某些内网服务，应显式配置 `allow` 规则，并尽量只放行必要的 `network`、`ip` 和 `port`。

若服务器端还需要依赖域名传递给 `sockopt` 的特性（例如 `sockopt.domainStrategy` 或 `happyEyeballs`），则不能继续依赖这套默认安全策略。可将第一条规则配置为不带任何匹配条件的 `allow`，以恢复此前行为；这样也等同于关闭这套默认安全策略，应自行评估安全影响。
:::

### FinalRuleObject

```json
{
  "action": "block",
  "network": "tcp,udp",
  "port": "53,443",
  "ip": ["10.0.0.0/8", "2001:db8::/32"],
  "blockDelay": "30-90"
}
```

规则中的各匹配条件为与关系；省略某个条件时，表示对此条件不作限制。

> `action`: "allow" | "block"

定义规则命中后的动作。

* `allow`: 放行目标。
* `block`: 阻止目标。

> `network`: "tcp" | "udp" | "tcp,udp"

匹配网络类型。当连接方式是指定方式时，此规则生效。也可以写成字符串数组，如 `["tcp", "udp"]`。省略时匹配所有网络。

> `port`: number | string

目标端口范围，写法与 [路由规则中的 `port`](../routing.md#ruleobject) 一致。省略时匹配所有端口。

> `ip`: \[string]

一个数组，数组内每一项代表一个 IP 范围。当某一项匹配目标 IP 时，此规则生效。写法与 [路由规则中的 `ip`](../routing.md#ruleobject) 一致。省略时匹配所有 IP。

> `blockDelay`: string

设置阻止规则命中后的黑洞持续时间。

当规则的 `action` 为 `block` 且命中目标时，Freedom 会让连接进入黑洞状态，并在这段时间结束后关闭连接。单位为秒，可以写成固定值或范围，例如 `30` 或 `30-90`。省略时默认为 `30-90` 即此范围内随机一个值。

---

---
url: /config/outbounds/http.md
---
# HTTP

HTTP 协议。

::: danger
**http 协议没有对传输加密，不适宜经公网中传输，更容易成为被人用作攻击的肉鸡。**
:::

::: tip
`http` 只能代理 tcp 协议，udp 系的协议均不能通过。
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "http",
      "settings": {
        // [!code focus:10]
        "address": "192.168.108.1",
        "port": 3128,
        "user": "my-username",
        "pass": "my-password",
        "level": 0,
        "email": "love@xray.com",
        "headers": {
          "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
          "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"
        }
      }
    }
  ]
}
```

::: tip
目前 HTTP 协议 outbound 中 `streamSettings` 设置 `security` 和 `tlsSettings` 是生效的。
:::

> `address`: string

HTTP 代理服务器地址，必填。

> `port`: int

HTTP 代理服务器端口，必填。

> `user`: string

用户名，字符串类型。如果对接服务端需要认证则必填，否则不要包含此项。

> `pass`: string

密码，字符串类型。如果对接服务端需要认证则必填，否则不要包含此项。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。如果对接服务端需要认证则选填，否则不要包含此项。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

邮件地址，用于标识用户。如果对接服务端需要认证则选填，否则不要包含此项。

> `headers`: map{ string, string }

HTTP 头，一个键值对，每个键表示一个 HTTP 头的名称，每次请求会附上所有的键值对。

---

---
url: /config/outbounds/loopback.md
---
# Loopback

Loopback 是个出站数据协议，其作用为将经该出站传出的数据重新送入路由入站，以达到数据无需离开 Xray-core 即可再次被路由处理的效果。

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "loopback",
      "settings": {
        // [!code focus:1]
        "inboundTag": "TagUseAsInbound"
      }
    }
  ]
}
```

> `inboundTag`: string

用于重新路由的入站协议标识。

该标识可以在路由中用于 `inboundTag` ，表示该出站中的数据可以被对应的路由规则再次处理。

### 如何使用？

如果需要将已经通过路由规则分流过的流量再由其它路由规则做更细致的分流，比如由同一组路由规则分流后的 TCP 流量和 UDP 要走不同的出站，则可以使用 `loopback` 出站完成。

```json
{
  "outbounds": [
    {
      "protocol": "loopback",
      "tag": "need-to-split",
      "settings": {
        "inboundTag": "traffic-input" // 该 tag 在下方用于 RuleObject 的 inboundTag
      }
    },
    {
      "tag": "tcp-output"
      // protocol, settings, streamSettings 之类的设置
    },
    {
      "tag": "udp-output"
      // protocol, settings, streamSettings 之类的设置
    }
  ],
  "routing": {
    "rules": [
      {
        "inboundTag": ["traffic-input"], // loopback 设定的 tag
        "network": "tcp",
        "outboundTag": "tcp-output"
      },
      {
        "inboundTag": ["traffic-input"], // loopback 设定的 tag
        "network": "udp",
        "outboundTag": "udp-output"
      }
    ]
  }
}
```

---

---
url: /config/outbounds/shadowsocks.md
---
# Shadowsocks

[Shadowsocks](https://zh.wikipedia.org/wiki/Shadowsocks) 协议，兼容大部分其它版本的实现。

目前兼容性如下：

* 支持 TCP 和 UDP 数据包转发，其中 UDP 可选择性关闭；
* 推荐的加密方式：
  * 2022-blake3-aes-128-gcm
  * 2022-blake3-aes-256-gcm
  * 2022-blake3-chacha20-poly1305
* 其他加密方式
  * aes-256-gcm
  * aes-128-gcm
  * chacha20-poly1305 或称 chacha20-ietf-poly1305
  * xchacha20-poly1305 或称 xchacha20-ietf-poly1305
  * none 或 plain

Shadowsocks 2022 新协议格式提升了性能并带有完整的重放保护，解决了旧协议的以下安全问题：

* [Shadowsocks AEAD 加密方式设计存在严重漏洞，无法保证通信内容的可靠性](https://github.com/shadowsocks/shadowsocks-org/issues/183)
* 原有 TCP 重放过滤器误报率随时间增加
* 没有 UDP 重放保护
* 可用于主动探测的 TCP 行为

::: danger
"none" 不加密方式下流量将明文传输。为确保安全性, 不要在公共网络上使用。
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "shadowsocks",
      "settings": {
        // [!code focus:8]
        "email": "love@xray.com",
        "address": "127.0.0.1",
        "port": 1234,
        "method": "加密方式",
        "password": "密码",
        "uot": true,
        "UoTVersion": 2,
        "level": 0
      }
    }
  ]
}
```

> `email`: string

邮件地址，可选，用于标识用户

> `address`: address

Shadowsocks 服务端地址，支持 IPv4、IPv6 和域名。必填。

> `port`: number

Shadowsocks 服务端端口。必填。

> `method`: string

Shadowsocks 加密方式，必填。

> `password`: string

Shadowsocks 认证密码，必填。

> `uot`: bool

启用`udp over tcp`。

> `UoTVersion`: number

`UDP over TCP` 的实现版本。

当前可选值：`1`, `2`

* Shadowsocks 2022

使用与 WireGuard 类似的预共享密钥作为密码。

使用 `openssl rand -base64 <长度>` 以生成与 shadowsocks-rust 兼容的密钥，长度取决于所使用的加密方法。

| 加密方法                      | 密钥长度 |
| ----------------------------- | -------: |
| 2022-blake3-aes-128-gcm       |       16 |
| 2022-blake3-aes-256-gcm       |       32 |
| 2022-blake3-chacha20-poly1305 |       32 |

在 Go 实现中，32 位密钥始终工作。

* 其他加密方法

任意字符串。不限制密码长度，但短密码会更可能被破解，建议使用 16 字符或更长的密码。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

`level` 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

---

---
url: /config/outbounds/socks.md
---
# Socks

标准 Socks 协议实现，兼容 Socks 5。

::: danger
**Socks 协议没有对传输加密，不适宜经公网中传输**
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "socks",
      "settings": {
        // [!code focus:6]
        "address": "127.0.0.1",
        "port": 1234,
        "user": "test user",
        "pass": "test pass",
        "level": 0,
        "email": "love@xray.com"
      }
    }
  ]
}
```

> `address`: address

服务器地址, 必填

::: tip
仅支持连接到 Socks 5 服务器。
:::

> `port`: number

服务器端口, 必填。

> `user`: string

用户名，字符串类型。如果对接服务端需要认证则必填，否则不要包含此项。

> `pass`: string

密码，字符串类型。如果对接服务端需要认证则必填，否则不要包含此项。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。如果对接服务端需要认证则选填，否则不要包含此项。

userLevel 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `email`: string

邮件地址，用于标识用户。如果对接服务端需要认证则选填，否则不要包含此项。

---

---
url: /config/outbounds/trojan.md
---
# Trojan

[Trojan](https://trojan-gfw.github.io/trojan/protocol) 协议

::: danger
Trojan 被设计工作在正确配置的加密 TLS 隧道
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "trojan",
      "settings": {
        // [!code focus:5]
        "address": "127.0.0.1",
        "port": 1234,
        "password": "password",
        "email": "love@xray.com",
        "level": 0
      }
    }
  ]
}
```

> `address`: address

服务端地址，支持 IPv4、IPv6 和域名。必填。

> `port`: number

服务端端口，通常与服务端监听的端口相同。

> `password`: string

密码. 必填，任意字符串。

> `email`: string

邮件地址，可选，用于标识用户

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

---

---
url: /config/outbounds/vless.md
---
# VLESS（XTLS Vision Seed）

VLESS 是一个无状态的轻量传输协议，它分为入站和出站两部分，可以作为 Xray 客户端和服务器之间的桥梁。

与 [VMess](./vmess.md) 不同，VLESS 不依赖于系统时间，认证方式同样为 UUID。

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "vless",
      "settings": {
        // [!code focus:7]
        "address": "example.com",
        "port": 443,
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
        "encryption": "none",
        "flow": "xtls-rprx-vision",
        "level": 0,
        "reverse": {}
      }
    }
  ]
}
```

> `address`: address

服务端地址，指向服务端，支持域名、IPv4、IPv6。

> `port`: number

服务端端口，通常与服务端监听的端口相同。

> `id`: string

VLESS 的用户 ID，可以是任意小于 30 字节的字符串, 也可以是一个合法的 UUID.
自定义字符串和其映射的 UUID 是等价的, 这意味着你将可以这样在配置文件中写 id 来标识同一用户,即

* 写 `"id": "我爱🍉老师1314"`,
* 或写 `"id": "5783a3e7-e373-51cd-8642-c83782b807c5"` (此 UUID 是 `我爱🍉老师1314` 的 UUID 映射)

其映射标准在 [VLESS UUID 映射标准：将自定义字符串映射为一个 UUIDv5](https://github.com/XTLS/Xray-core/issues/158)

你可以使用命令 `xray uuid -i "自定义字符串"` 生成自定义字符串所映射的的 UUID，也可以使用命令 `xray uuid` 生成随机的 UUID。

> `encryption`: "none"

[VLESS 加密](https://github.com/XTLS/Xray-core/pull/5067)设置。不能留空，禁用需显式设置为 `"none"`.

推荐大多数用户使用命令 `xray vlessenc` 自动生成该字段确保编写不会出错。下方详细配置仅推荐高级用户阅读。

::: details 详细配置
其格式为一串字段由 `.` 连接的详细配置。 如 `mlkem768x25519plus.native.0rtt.100-111-1111.75-0-111.50-0-3333.ptjHQxBQxTJ9MWr2cd5qWIflBSACHOevTauCQwa_71U`. 本文档将用点分开的单独部分称之为一个块

* 第1个块为握手方式，目前有且仅有 `mlkem768x25519plus`. 要求服务端与客户端一致
* 第2个块为加密方式，可选 `native`/`xorpub`/`random`, 分别对应: 原始格式数据包/原始格式+混淆公钥部分/全随机数（类似 VMESS/Shadows socks）。要求服务端与客户端一致
* 第3个块为是否会话恢复。选择 `0rtt` 将跟随服务端设置尝试使用先前生成的票据跳过握手快速连接（可被服务端手动禁用），选择 `1rtt` 将强制执行 1RTT 的握手过程。此处与服务端设置含义不同，详见 VLESS 入站 `decryption` 设置。

往后为 padding, 连接建立后客户端发送一些垃圾数据用以混淆长度特征，无需与服务端相同（入站的相同部分为服务端向客户端方向发送的 padding），属于可变长部分，格式为 `padding.delay.padding`+`(.delay.padding)`×n（可插入多个 padding, 要求两个 padding 块之间必须包含一个 delay 块） 比如可以写一个超长的 `padding.delay.padding.delay.padding.delay.padding.delay.padding.delay.padding`

* `padding` 格式为 `probability-min-max` 如 `100-111-1111` 含义为 100% 发送一个长度 111~1111 的padding.
* `delay` 格式同样为 `probability-min-max` 如 `75-0-111` 含义为 75% 的概率等待 0~111 毫秒

第一个 padding 块存在特殊要求，要求概率为 100% 且最小长度大于 0. 若不存在任何 padding, 核心自动使用 `100-111-1111.75-0-111.50-0-3333` 作为 padding 设置。

最后一个块会被核心识别为认证服务端使用的参数，可用 `./xray x25519`（使用 Password 部分） 或 `./xray mlkem768`（使用 Client 部分） 生成，要求与服务端对应。`mlkem768` 属于后量子算法，可以防止（未来）客户端参数泄露后被量子计算机破解出私钥并冒充服务端。该参数仅用于验证，握手过程无论如何都是后量子安全的，现有的加密数据无法被未来出现的量子计算机破解。
:::

> `flow`: string

流控模式，用于选择 XTLS 的算法。

目前出站协议中有以下流控模式可选：

* 无 `flow` 或者 空字符： 使用普通 TLS 代理
* `xtls-rprx-vision`：使用 XTLS，包含内层握手随机填充。会拦截目标为 443 端口的 UDP 流量（QUIC），促使浏览器使用普通 HTTPS 以增加可以被 Splice 的流量。
* `xtls-rprx-vision-udp443`：同 `xtls-rprx-vision`, 但是不会拦截 UDP 443，用于有程序强制使用 QUIC，被拦截会导致其无法工作时。

XTLS 仅在以下搭配下可用

* TCP+TLS/Reality 此时若传输的是 TLS 1.3，核心将尝试在底层 Splice 加密后的数据，若成功将节省全部的核心 IO 开销。
* VLESS Encryption 无底层传输限制，若底层不为 TCP 则仅尝试穿透 Encryption，节省 Encryption 的开销，如果是 TCP 则仍将尝试进行 Splice.

::: tip 关于 Splice
Splice 是 Linux Kernel 提供的函数，系统内核直接转发 TCP，不再经过 Xray 的内存，大大减少了数据拷贝、CPU 上下文切换的次数。

使用 Vision 模式时，如果满足下面条件，会自动启用 Splice.

* Linux 环境
* 入站协议为 `Dokodemo door`、`Socks`、`HTTP` 等纯净的 TCP 连接, 或其它使用了 XTLS 的入站协议
* 出站协议为 VLESS + XTLS

使用 Splice 时网速显示会滞后，连接断开后才会计入统计，因为在内核接管连接期间核心无法知道它的流量情况。
:::

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `reverse`: struct

VLESS 极简反向代理配置，会保留公网端的真实源 IP 信息。

存在此项代表该出站可以被用作 VLESS 反向代理出站，其会自动向服务端建立连接注册反向代理隧道。

```json
"reverse": {
  "tag": "r-inbound",
  "sniffing" : {}
}
```

`tag` 为该反向代理的入站代理 tag. 当服务端派发反向代理请求时会从使用这个 tag 的入站进入路由系统，使用路由系统将其路由到你需要的出站。

使用的 UUID 需要是服务端同样配置了 `reverse` 的 UUID（详见 VLESS 入站）。

`sniffing` 见 [sniffingObject](../inbound.md#sniffingobject) 对从该反向代理进入的请求执行嗅探。

::: tip
完整教程：[VLESS 反向代理示例](../../document/level-2/vless_reverse.md)
:::

---

---
url: /config/outbounds/vmess.md
---
# VMess

[VMess](../../development/protocols/vmess.md) 是一个加密传输协议，通常作为 Xray 客户端和服务器之间的桥梁。

::: danger
VMess 依赖于系统时间，请确保使用 Xray 的系统 UTC 时间误差在 120 秒之内，时区无关。在 Linux 系统中可以安装`ntp`服务来自动同步系统时间。
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "vmess",
      "settings": {
        // [!code focus:6]
        "address": "127.0.0.1",
        "port": 37192,
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
        "security": "auto",
        "level": 0,
        "experiments": ""
      }
    }
  ]
}
```

> `address`: address

服务端地址，支持 IP 地址或者域名。

> `port`: number

服务端监听的端口号, 必填。

> `id`：string

Vmess 的用户 ID，可以是任意小于 30 字节的字符串, 也可以是一个合法的 UUID.

自定义字符串和其映射的 UUID 是等价的, 这意味着你将可以这样在配置文件中写 id 来标识同一用户,即

* 写 `"id": "我爱🍉老师1314"`,
* 或写 `"id": "5783a3e7-e373-51cd-8642-c83782b807c5"` (此 UUID 是 `我爱🍉老师1314` 的 UUID 映射)

其映射标准在 [VLESS UUID 映射标准：将自定义字符串映射为一个 UUIDv5](https://github.com/XTLS/Xray-core/issues/158)

你可以使用命令 `xray uuid -i "自定义字符串"` 生成自定义字符串所映射的的 UUID, 也可以使用命令 `xray uuid` 生成随机的 UUID。

> `level`: number

用户等级，连接会使用这个用户等级对应的 [本地策略](../policy.md#levelpolicyobject)。

level 的值, 对应 [policy](../policy.md#policyobject) 中 `level` 的值。 如不指定, 默认为 0。

> `security`: "aes-128-gcm" | "chacha20-poly1305" | "auto" | "none" | "zero"

加密方式，客户端将使用配置的加密方式发送数据，服务器端自动识别，无需配置。

* `"aes-128-gcm"`：使用 AES-128-GCM 算法
* `"chacha20-poly1305"`：使用 Chacha20-Poly1305 算法
* `"auto"`：默认值，自动选择（运行框架为 AMD64、ARM64 或 s390x 时为 aes-128-gcm 加密方式，其他情况则为 Chacha20-Poly1305 加密方式）
* `"none"`：不加密，保持vmess消息结构
* `"zero"`：不加密，数据流直接拷贝(类似vless)

不推荐在未开启 TLS 加密并强制校验证书的情况下使用 `"none"` `"zero"` 伪加密方式。无论使用哪种加密方式， VMess 的包头都会受到加密和认证的保护。

注意，auto 只是判断客户端的 AES 硬件加速支持状态，如果服务端没有 AES 硬件加速支持，仍然需要手动设置到 chacha20-poly1305, 这非常重要，因为 Chacha20-Poly1305 在支持 AES 加速的平台上比 AES-128-GCM 耗时多了约 48% 但是在不支持 AES 加速的平台上, AES-128-GCM 的耗时将比 Chacha20-Poly1305 高出 2000% 以上。

> `experiments`: string

启用的 VMess 协议实验性功能。（此处的功能为不稳定功能， 可能随时被移除）多个启用的实验之间可以用 | 字符分割，如 "AuthenticatedLength|NoTerminationSignal" 。

"AuthenticatedLength" 启用认证的数据包长度实验。此实验需要同时在客户端与服务器端同时开启，并运行相同版本的程序。

"NoTerminationSignal" 启用不发送断开连接标志。该特性现默认启用。

---

---
url: /config/outbounds/wireguard.md
---
# Wireguard

标准 Wireguard 协议实现。

::: danger
**Wireguard 协议并非专门为翻墙而设计，若在最外层过墙，存在特征可能导致服务器被封锁**
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "wireguard",
      "settings": {
        // [!code focus:18]
        "secretKey": "PRIVATE_KEY",
        "address": [
          // optional, default ["10.0.0.1", "fd59:7153:2388:b5fd:0000:0000:0000:0001"]
          "IPv4_CIDR",
          "IPv6_CIDR",
          "and more..."
        ],
        "peers": [
          {
            "endpoint": "ENDPOINT_ADDR",
            "publicKey": "PUBLIC_KEY"
          }
        ],
        "noKernelTun": false,
        "mtu": 1420, // optional, default 1420
        "reserved": [1, 2, 3],
        "workers": 2, // optional, default runtime.NumCPU()
        "domainStrategy": "ForceIP"
      }
    }
  ]
}
```

::: tip
目前 Wireguard 协议 outbound 中不支持设置 `streamSettings`
:::

> `secretKey`: string

用户私钥。必填。

> `address`: string array

Wireguard 会在本地开启虚拟网卡 tun。使用一个或多个 IP 地址，支持 IPv6。

> `noKernelTun`: true | false

默认情况下核心会检测是否处于 Linux 并且当前用户具有 CAP\_NET\_ADMIN 权限决定是否启用系统虚拟网卡，否则使用 gvisor, 使用系统虚拟网卡相对性能更高。注意这只是用来处理 IP 包的，和 wireguard kernel module 没有任何关系。

这个判断不一定准确，比如一些 lxc 虚拟化可能本来就没有 TUN 权限，这会导致出站无法工作，所以可以在这里设置是否手动禁用。

使用系统虚拟网卡时会占用 IPv6 的 10230 号路由表，每一个其他 wireguard 出站会依次往后使用路由表，比如第二个会使用 10231 号路由表，以此类推。

注意如果在同一个机器上启动第二个 Xray 实例不会接着分配路由表号，会继续尝试使用 10230 号路由表，因为已经被第一个 Xray 实例占用所以会失败无法连接，如果实在需要也需要设置这个选项禁用系统虚拟网卡。

> `mtu`: int

Wireguard 底层 tun 的MTU大小。

一个 Wireguard 数据包的结构如下

```
- 20-byte IPv4 header or 40 byte IPv6 header
- 8-byte UDP header
- 4-byte type
- 4-byte key index
- 8-byte nonce
- N-byte encrypted data
- 16-byte authentication tag
```

`N-byte encrypted data` 即为我们需要的 MTU 的值，根据 endpoint 是 IPv4 还是 IPv6，具体的值可以是 1440(IPv4) 或者 1420(IPv6)，如果处于特殊环境下再额外减掉即可 (如家宽 PPPoE 额外 -8)。

> `reserved` \[ number ]

Wireguard 保留字节，按需填写。

> `workers`: int

Wireguard 使用线程数，默认为系统的核心数。

> `peers`: \[ [Peers](#peers) ]

Wireguard 服务器列表，其中每一项是一个服务器配置。

> `domainStrategy`: "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4" | "ForceIP"

当 Wireguard 服务器地址为域名、被代理流量目标地址是域名时，控制它们的域名解析策略。

不像绝大多数代理协议，Wireguard 不允许传递域名作为目标，所以如果传入目标为一域名需要解析为 IP 地址后传送，这会经由 Xray 内置DNS处理，此处字段含义见 `Freedom` 出站的 `domainStrategy`，默认值为 `ForceIP`。

`Freedom` 出站的 `domainStrategy` 包含诸如 `UseIP` 的选项，在这里不提供，因为 Wiregiard 必须获取一个可用的 IP，不能执行 `UseIP` 解析失败后回落为域名的行为。
注意：作用于被代理流量时，此选项还受 `address` 选项的约束，比如你设置了 ForceIPv6v4 但是 address 中没有设置 IPv6 地址，尽管目标域名有 AAAA 记录也不会解析。

### Peers

```json
{
  "endpoint": "ENDPOINT_ADDR",
  "publicKey": "PUBLIC_KEY",
  "preSharedKey": "PRE_SHARED_KEY", // optional, default "0000000000000000000000000000000000000000000000000000000000000000"
  "keepAlive": 0, // optional, default 0
  "allowedIPs": ["0.0.0.0/0"] // optional, default ["0.0.0.0/0", "::/0"]
}
```

> `endpoint`: address

服务器地址, 必填。

URL: 端口 格式，例如 `engage.cloudflareclient.com:2408`
IP: 端口 格式，例如 `162.159.192.1:2408` 或 `[2606:4700:d0::a29f:c001]:2408`

> `publicKey`: string

服务器公钥，用于验证, 必填。

> `preSharedKey`: string

额外的对称加密密钥。

> `keepAlive`: int

心跳包时间间隔，单位为秒，默认为 0 表示无心跳。

> `allowedIPs`: string array

Wireguard 仅允许特定源 IP 的流量。

---

---
url: /config/outbounds/hysteria.md
---
# Hysteria

Hysteria 协议的客户端实现。

这个页面非常简单，因为 hysteria 协议实际上分为一个简单的代理控制协议和经过调优的 QUIC 底层传输，在 Xray 中代理协议和底层传输被拆分，更多内容（如 brutal）详见底层传输的 [hysteriaSettings](../transports/hysteria.md) [finalmask.quicParams](../transport.md#quicParams)

::: tip
`hysteria protocol` 本身无认证，搭配非 `hysteria` 传输层将无法代理 `udp`，也不推荐搭配其他传输层
:::

## OutboundConfigurationObject

`OutboundConfigurationObject` 对应 [`OutboundObject`](../outbound.md) 中的 `settings` 项。

```json
{
  "outbounds": [
    {
      // ...
      "protocol": "hysteria",
      "settings": {
        // [!code focus:3]
        "version": 2,
        "address": "192.168.108.1",
        "port": 3128
      }
    }
  ]
}
```

> `version`: number

Hysteria 版本，必须为 2。

> `address`: string

Hysteria 代理服务器地址，必填。

> `port`: int

Hysteria 代理服务器端口，必填。

---

---
url: /config/transports.md
---
# Xray 传输层列表

Xray 支持以下传输层：

* [RAW](raw.md)
* [XHTTP: Beyond REALITY](xhttp.md)
* [mKCP](mkcp.md)
* [gRPC](grpc.md)
* [WebSocket](websocket.md)
* [HTTPUpgrade](httpupgrade.md)
* [Hysteria](hysteria.md)

---

---
url: /config/transports/raw.md
---
# RAW

更名自曾经的 TCP 传输层（原名称有歧义），出站 RAW 传输层直接发送经代理协议包装后产生的 TCP、UDP 数据，核心不使用其它传输层（如 [XHTTP](https://github.com/XTLS/Xray-core/discussions/4113)）承载其流量。

可以和各种协议有多种组合模式。

## RawObject

`RawObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `rawSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "raw",
        "rawSettings": {
          // [!code focus:4]
          "acceptProxyProtocol": false,
          "header": {
            "type": "none"
          }
        }
      }
    }
  ]
}
```

> `acceptProxyProtocol`: true | false

仅用于 inbound，指示是否接收 PROXY protocol。

[PROXY protocol](https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt) 专用于传递请求的真实来源 IP 和端口，**若你不了解它，请先忽略该项**。

常见的反代软件（如 HAProxy、Nginx）都可以配置发送它，VLESS fallbacks xver 也可以发送它。

填写 `true` 时，最底层 TCP 连接建立后，请求方必须先发送 PROXY protocol v1 或 v2，否则连接会被关闭。

默认值为 `false`。

> `header`: [NoneHeaderObject](#noneheaderobject) | [HttpHeaderobject](#httpheaderobject)

数据包头部伪装设置，默认值为 `NoneHeaderObject`。

::: tip
HTTP 伪装无法被其它 HTTP 服务器（如 Nginx）分流，但可以被 VLESS fallbacks path 分流。
:::

### NoneHeaderObject

不进行伪装

```json
{
  "type": "none"
}
```

> `type`: "none"

指定不进行伪装

### HttpHeaderObject

HTTP 伪装配置必须在对应的入站出站连接上同时配置，且内容必须一致。

```json
{
  "type": "http",
  "request": {},
  "response": {}
}
```

> `type`: "http"

指定进行 HTTP 伪装

> `request`: [HTTPRequestObject](#httprequestobject)

HTTP 请求

> `response`: [HTTPResponseObject](#httpresponseobject)

HTTP 响应

#### HTTPRequestObject

```json
{
  "version": "1.1",
  "method": "GET",
  "path": ["/"],
  "headers": {
    "Host": ["www.baidu.com", "www.bing.com"],
    "User-Agent": [
      "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
      "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
    ],
    "Accept-Encoding": ["gzip, deflate"],
    "Connection": ["keep-alive"],
    "Pragma": "no-cache"
  }
}
```

> `version`: string

HTTP 版本，默认值为 `"1.1"`。

> `method`: string

HTTP 方法，默认值为 `"GET"`。

> `path`: \[ string ]

路径，一个字符串数组。默认值为 `["/"]`。当有多个值时，每次请求随机选择一个值。

> `headers`: map{ string, \[ string ]}

HTTP 头，一个键值对，每个键表示一个 HTTP 头的名称，对应的值是一个数组。

每次请求会附上所有的键，并随机选择一个对应的值。默认值见上方示例。

#### HTTPResponseObject

```json
{
  "version": "1.1",
  "status": "200",
  "reason": "OK",
  "headers": {
    "Content-Type": ["application/octet-stream", "video/mpeg"],
    "Transfer-Encoding": ["chunked"],
    "Connection": ["keep-alive"],
    "Pragma": "no-cache"
  }
}
```

> `version`: string

HTTP 版本，默认值为 `"1.1"`。

> `status`: string

HTTP 状态，默认值为 `"200"`。

> `reason`: string

HTTP 状态说明，默认值为 `"OK"`。

> `headers`: map {string, \[ string ]}

HTTP 头，一个键值对，每个键表示一个 HTTP 头的名称，对应的值是一个数组。

每次请求会附上所有的键，并随机选择一个对应的值。默认值见上方示例。

---

---
url: /config/transports/xhttp.md
---
# XHTTP: Beyond REALITY

See [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

---

---
url: /config/transports/mkcp.md
---
# mKCP

mKCP 使用 UDP 来模拟 TCP 连接。

mKCP 牺牲带宽来降低延迟。传输同样的内容，mKCP 一般比 TCP 消耗更多的流量。

::: tip
请确定主机上的防火墙配置正确
:::

## KcpObject

`KcpObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `kcpSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "mkcp",
        "kcpSettings": {
          // [!code focus:7]
          "mtu": 1350,
          "tti": 20,
          "uplinkCapacity": 5,
          "downlinkCapacity": 20,
          "congestion": false,
          "readBufferSize": 1,
          "writeBufferSize": 1
        }
      }
    }
  ]
}
```

::: tip
`header` 和 `seed` 字段已被移除，请使用 [FinalMask](../transport.md#finalmaskobject) 进行配置。

并且曾经默认的 mKCP 混淆也被移除，要连接旧版服务端，需要在 FinalMask 中配置 `mkcp-original`。
:::

> `mtu`: number

最大传输单元（maximum transmission unit）
请选择一个介于 576 - 1460 之间的值。

默认值为 `1350`。

> `tti`: number

传输时间间隔（transmission time interval），单位毫秒（ms），mKCP 将以这个时间频率发送数据。
请选译一个介于 10 - 100 之间的值。

默认值为 `50`。

> `uplinkCapacity`: number

上行链路容量，即主机发出数据所用的最大带宽，单位 MB/s，注意是 Byte 而非 bit。
可以设置为 0，表示一个非常小的带宽。

默认值 `5`。

> `downlinkCapacity`: number

下行链路容量，即主机接收数据所用的最大带宽，单位 MB/s，注意是 Byte 而非 bit。
可以设置为 0，表示一个非常小的带宽。

默认值 `20`。

::: tip
`uplinkCapacity` 和 `downlinkCapacity` 决定了 mKCP 的传输速度。
以客户端发送数据为例，客户端的 `uplinkCapacity` 指定了发送数据的速度，而服务器端的 `downlinkCapacity` 指定了接收数据的速度。两者的值以较小的一个为准。

推荐把 `downlinkCapacity` 设置为一个较大的值，比如 100，而 `uplinkCapacity` 设为实际的网络速度。当速度不够时，可以逐渐增加 `uplinkCapacity` 的值，直到带宽的两倍左右。
:::

> `congestion`: true | false

是否启用拥塞控制。

开启拥塞控制之后，Xray 会自动监测网络质量，当丢包严重时，会自动降低吞吐量；当网络畅通时，也会适当增加吞吐量。

默认值为 `false`

> `readBufferSize`: number

单个连接的读取缓冲区大小，单位是 MB。

默认值为 `2`。

> `writeBufferSize`: number

单个连接的写入缓冲区大小，单位是 MB。

默认值为 `2`。

::: tip
`readBufferSize` 和 `writeBufferSize` 指定了单个连接所使用的内存大小。
在需要高速传输时，指定较大的 `readBufferSize` 和 `writeBufferSize` 会在一定程度上提高速度，但也会使用更多的内存。

在网速不超过 20MB/s 时，默认值 1MB 可以满足需求；超过之后，可以适当增加 `readBufferSize` 和 `writeBufferSize` 的值，然后手动平衡速度和内存的关系。
:::

## 鸣谢

* [@skywind3000](https://github.com/skywind3000) 发明并实现了 KCP 协议。
* [@xtaci](https://github.com/xtaci) 将 KCP 由 C 语言实现翻译成 Go。
* [@xiaokangwang](https://github.com/xiaokangwang) 测试 KCP 与 Xray 的整合并提交了最初的 PR。

## 对 KCP 协议的改进

### 更小的协议头

原生 KCP 协议使用了 24 字节的固定头部，而 mKCP 修改为数据包 18 字节，确认（ACK）包 16 字节。更小的头部有助于躲避特征检查，并加快传输速度。

另外，原生 KCP 的单个确认包只能确认一个数据包已收到，也就是说当 KCP 需要确认 100 个数据已收到时，它会发出 24 \* 100 = 2400 字节的数据。其中包含了大量重复的头部数据，造成带宽的浪费。mKCP 会对多个确认包进行压缩，100 个确认包只需要 16 + 2 + 100 \* 4 = 418 字节，相当于原生的六分之一。

### 确认包重传

原生 KCP 协议的确认（ACK）包只发送一次，如果确认包丢失，则一定会导致数据重传，造成不必要的带宽浪费。而 mKCP 会以一定的频率重发确认包，直到发送方确认为止。单个确认包的大小为 22 字节，相比起数据包的 1000 字节以上，重传确认包的代价要小得多。

### 连接状态控制

mKCP 可以有效地开启和关闭连接。当远程主机主动关闭连接时，连接会在两秒钟之内释放；当远程主机断线时，连接会在最多 30 秒内释放。

原生 KCP 不支持这个场景。

---

---
url: /config/transports/grpc.md
---
# gRPC

基于 gRPC 的传输方式。

它基于 HTTP/2 协议，理论上可以通过其它支持 HTTP/2 的服务器（如 Nginx）进行中转。
gRPC（HTTP/2）内置多路复用，不建议使用 gRPC 与 HTTP/2 时启用 mux.cool。

::: danger
**推荐换用 [XHTTP](https://github.com/XTLS/Xray-core/discussions/4113)，其相较于 gRPC 的优势已在 STREAM-UP/ONE 小节中注明。**
:::

::: warning ⚠⚠⚠

* gRPC 不支持指定 Host。请在出站代理地址中填写 **正确的域名** ，或在 `(x)tlsSettings` 中填写 `ServerName`，否则无法连接。
* gRPC 不支持回落到其他服务。
* gRPC 服务存在被主动探测的风险。建议使用 Caddy 或 Nginx 等反向代理工具，通过 Path 前置分流。
  :::

::: tip
如果您使用 Caddy 或 Nginx 等反向代理，请注意下列事项：

* 请确定反向代理服务器开启了 HTTP/2
* 请使用 HTTP/2 或 h2c (Caddy)，grpc\_pass (Nginx) 连接到 Xray。
* 普通模式的 Path 为 `/${serviceName}/Tun`, Multi 模式为 `/${serviceName}/TunMulti`
* 如果需要接收客户端 IP，可以通过由 Caddy / Nginx 发送 `X-Real-IP` header 来传递客户端 IP。
  :::

::: tip
如果你正在使用回落，请注意下列事项：

* 不建议回落到 gRPC，存在被主动探测的风险。
* 请确认`h2` 位于 (x)tlsSettings.alpn 中的第一顺位，否则 gRPC（HTTP/2）可能无法完成 TLS 握手。
* gRPC 无法通过进行 Path 分流。
  :::

## GRPCObject

`GRPCObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `grpcSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "grpc",
        "grpcSettings": {
          // [!code focus:8]
          "authority": "grpc.example.com",
          "serviceName": "name",
          "multiMode": false,
          "user_agent": "custom user agent",
          "idle_timeout": 60,
          "health_check_timeout": 20,
          "permit_without_stream": false,
          "initial_windows_size": 0
        }
      }
    }
  ]
}
```

> `authority`: string

一个字符串，可以当 Host 来用，实现一些其它用途。

> `serviceName`: string

一个字符串，指定服务名称，**类似于** HTTP/2 中的 Path。
客户端会使用此名称进行通信，服务端会验证服务名称是否匹配。

::: tip
当 `serviceName` 起始为斜杠时可以自定义 path，至少要两个斜杠。
例如在服务端填写 `"serviceName": "/my/sample/path1|path2"`，客户端可填写 `"serviceName": "/my/sample/path1"` 或 `"/my/sample/path2"`。
:::

> `user_agent`: string

::: tip
**只需**在**出站**（**客户端**）配置。
:::

设置 gRPC 的用户代理，可能能防止某些 CDN 阻止 gRPC 流量。

> `multiMode`: true | false&#x20;

`true` 启用 `multiMode`，默认值为： `false`。

这是一个 **实验性** 选项，可能不会被长期保留，也不保证跨版本兼容。此模式在 **测试环境中** 能够带来约 20% 的性能提升，实际效果因传输速率不同而不同。

::: tip
**只需**在**出站**（**客户端**）配置。
:::

> `idle_timeout`: number

单位秒，当这段时间内没有数据传输时，将会进行健康检查。如果此值设置为 `10` 以下，将会使用 `10`，即最小值。

::: tip
如果没有使用 Caddy 或 Nginx 等反向代理工具（**通常不会**），设为 `60` 以下，服务端可能发送意外的 h2 GOAWAY 帧以关闭现有连接。
:::

健康检查默认**不启用**。

::: tip
**只需**在**出站**（**客户端**）配置。
:::

::: tip
可能会解决一些“断流”问题。
:::

> `health_check_timeout`: number

单位秒，健康检查的超时时间。如果在这段时间内没有完成健康检查，且仍然没有数据传输时，即认为健康检查失败。默认值为 `20`。

::: tip
**只需**在**出站**（**客户端**）配置。
:::

> `permit_without_stream`: true | false

`true` 允许在没有子连接时进行健康检查。默认值为 `false`。

::: tip
**只需**在**出站**（**客户端**）配置。
:::

> `initial_windows_size`: number

h2 Stream 初始窗口大小。当值小于等于 `0` 时，此功能不生效。当值大于 `65535` 时，动态窗口机制（Dynamic Window）会被禁用。默认值为 `0`，即不生效。

::: tip
**只需**在**出站**（**客户端**）配置。
:::

::: tip
通过 Cloudflare CDN 时，可将值设为 `65536` 及以上，即禁用动态窗口机制（Dynamic Window），可防止 Cloudflare CDN 发送意外的 h2 GOAWAY 帧以关闭现有连接。
:::

---

---
url: /config/transports/websocket.md
---
# WebSocket

使用标准的 WebSocket 来传输数据。

WebSocket 连接可以被其它 HTTP 服务器（如 Nginx）分流，也可以被 VLESS fallbacks path 分流。

::: danger
**推荐换用 [XHTTP](https://github.com/XTLS/Xray-core/discussions/4113)，以避免 WebSocket “ALPN 是 http/1.1” 等显著流量特征。**
:::

::: tip
Websocket 会识别 HTTP 请求的 X-Forwarded-For 头来覆写流量的源地址，优先级高于 PROXY protocol。
:::

## WebSocketObject

`WebSocketObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `wsSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "websocket",
        "wsSettings": {
          // [!code focus:7]
          "acceptProxyProtocol": false,
          "path": "/",
          "host": "xray.com",
          "headers": {
            "key": "value"
          },
          "heartbeatPeriod": 10
        }
      }
    }
  ]
}
```

> `acceptProxyProtocol`: true | false

仅用于 inbound，指示是否接收 PROXY protocol。

[PROXY protocol](https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt) 专用于传递请求的真实来源 IP 和端口，**若你不了解它，请先忽略该项**。

常见的反代软件（如 HAProxy、Nginx）都可以配置发送它，VLESS fallbacks xver 也可以发送它。

填写 `true` 时，最底层 TCP 连接建立后，请求方必须先发送 PROXY protocol v1 或 v2，否则连接会被关闭。

> `path`: string

WebSocket 所使用的 HTTP 协议路径，默认值为 `"/"`。

如果客户端路径中包含 `ed` 参数(如 `/mypath?ed=2560`)，将会启用 `Early Data` 以降低延迟，在升级的同时使用 `Sec-WebSocket-Protocol` 头承载首包数据，其值为首包长度阈值。如果首包长度超过此值，就不会启用 `Early Data`。推荐值为 2560，最大值为8192，过大的值可能导致部分兼容问题，如果遇到兼容性问题，可以尝试调低阈值。

> `host`: string

WebSocket 的HTTP请求中所发送的host，默认值为空。若服务端值为空时，不验证客户端发送来的host值。

当在服务端指定该值，或在 `headers` 中指定host，将会校验与客户端请求host是否一致。

客户端选择发送的host优先级 `host` > `headers` > `address`

> `headers`: map {string: string}

仅客户端，自定义 HTTP 头，一个键值对，每个键表示一个 HTTP 头的名称，对应的值是字符串。

默认值为空。

> `heartbeatPeriod`: int

指定间隔固定时间发送一个 Ping message 保活连接。不指定或指定为0时不发送 Ping message，为当前默认行为。

## Browser Dialer

使用浏览器处理 TLS，详见 [Browser Dialer](../features/browser_dialer.md)

---

---
url: /config/transports/httpupgrade.md
---
# HTTPUpgrade

一个实现了类似于 WebSocket 进行 HTTP 1.1 升级请求和响应的协议，这使得它可以像 WebSocket 一样可以被CDN或者Nginx进行反代，但无需实现 WebSocket 协议的其他部分，所以具有更高的效率。
其设计不推荐单独使用，而是和TLS等安全协议一起工作。

::: danger
**推荐换用 [XHTTP](https://github.com/XTLS/Xray-core/discussions/4113)，以避免 HTTPUpgrade “ALPN 是 http/1.1” 等显著流量特征。**
:::

## HttpUpgradeObject

`HttpUpgradeObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `httpupgradeSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "httpupgrade",
        "httpupgradeSettings": {
          // [!code focus:6]
          "acceptProxyProtocol": false,
          "path": "/",
          "host": "xray.com",
          "headers": {
            "key": "value"
          }
        }
      }
    }
  ]
}
```

> `acceptProxyProtocol`: true | false

仅用于 inbound，指示是否接收 PROXY protocol。

[PROXY protocol](https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt) 专用于传递请求的真实来源 IP 和端口，**若你不了解它，请先忽略该项**。

常见的反代软件（如 HAProxy、Nginx）都可以配置发送它，VLESS fallbacks xver 也可以发送它。

填写 `true` 时，最底层 TCP 连接建立后，请求方必须先发送 PROXY protocol v1 或 v2，否则连接会被关闭。

> `path`: string

HTTPUpgrade 所使用的 HTTP 协议路径，默认值为 `"/"`。

如果客户端路径中包含 `ed` 参数(如 `/mypath?ed=2560`)，将会启用 `Early Data` 以降低延迟，其值为首包长度阈值。如果首包长度超过此值，就不会启用 `Early Data`。建议的值为2560。

> `host`: string

HTTPUpgrade 的HTTP请求中所发送的host，默认值为空。若服务端值为空时，不验证客户端发送来的host值。

当在服务端指定该值，或在 `headers` 中指定host，将会校验与客户端请求host是否一致。

客户端选择发送的host优先级 `host` > `headers` > `address`

> `headers`: map {string: string}

仅客户端，自定义 HTTP 头，一个键值对，每个键表示一个 HTTP 头的名称，对应的值是字符串。

默认值为空。

---

---
url: /config/transports/hysteria.md
---
# Hysteria

Hysteria2 的底层 QUIC 传输的 Xray 实现，通常搭配 hysteria[出站](../outbounds/hysteria.md) 和 hysteria[入站](../inbounds/hysteria.md) 使用，此时兼容其官方实现。

## HysteriaObject

`HysteriaObject` 对应 [`StreamSettingsObject`](../transport.md#streamsettingsobject) 中的 `hysteriaSettings` 项。

```json
{
  // outbound 示例，同样可用于 inbound
  "outbounds": [
    {
      // ...
      "streamSettings": {
        "network": "hysteria",
        "hysteriaSettings": {
          // [!code focus:14]
          "version": 2,
          "auth": "password",
          "udpIdleTimeout": 60,
          "masquerade": {
            "type": "",
            "dir": "",
            "url": "",
            "rewriteHost": false,
            "insecure": false,
            "content": "",
            "headers": {
              "key": "value"
            },
            "statusCode": 0
          }
        }
      }
    }
  ]
}
```

> `version`: number

Hysteria 版本，必须为 2。

> `auth`: string

Hysteria 认证密码，服务端和客户端需要保持一致。

搭配 `hysteria inbound` 时将会被 `users` 覆盖 (如果存在)

> `udpIdleTimeout`: number

单位秒，默认 60。

单条 `quic native udp` 连接空闲等待时间，过大应该不会严格遵守，可能会先被 policy 掐断。

> `masquerade`: [MasqObject](#MasqObject)

http3 页面伪装。

### MasqObject

```json
{
  "type": "",

  "dir": "",

  "url": "",
  "rewriteHost": false,
  "insecure": false,

  "content": "",
  "headers": {
    "key": "value"
  },
  "statusCode": 0
}
```

> `type`: "file" | "proxy" | "string"

不填为默认的 404 页面。

> `dir`: string

type 为 file 时的配置项。

> `url`: string

type 为 proxy 时的配置项。

> `rewriteHost`: false | true

type 为 proxy 时的配置项。

> `insecure`: false | true

type 为 proxy 时的配置项。

> `content`: string

type 为 string 时的配置项。

> `headers`: map{ string, string }

type 为 string 时的配置项。

> `statusCode`: int

type 为 string 时的配置项。

---

---
url: /document.md
---
# 快速入门

> **这个章节将告诉您如何用最简单的方式获得 Xray，并且开始使用 Xray。**

::: tip
这个部分有相当久没有更新了，有的地方可能不是很靠谱，我们只会尽量保持配置文件的文档更新，如果有地方踩坑了有修改建议欢迎 PR 修正。

ℱ.
:::

## 下载安装

Xray 支持各种平台，并且您可以从多种渠道和方式获得 Xray 的各种版本。

请点击 [下载安装](./install.md) 以获取 Xray

## 配置运行

下载并安装 Xray 后，只需对他进行配置即可使用。

请点击 [配置运行](./config.md) 以学习最简单的配置方式。

## 命令参数

Xray 有多种命令和参数可用,因此变得灵活和强大。

请点击 [命令参数](./command.md) 查看 Xray 的更多命令和参数用法。

## 改进文档

如果你有兴趣，请点击 [使用文档](./document.md) 帮助我们改进文档，或者点击页面下方的 `帮助我们改善此页面！`

我们十分感谢每一位 Contributor 作出的贡献！是你们让 Project X 变得更加强大！

## 小小白白话文

给予新手指导的使用心得

请点击 [小小白白话文](./level-0/) 以进行查看。

## 入门技巧

具备了基础之后，你就可以通过 [入门技巧](./level-1/) 来探索更多的使用方式了。

## 进阶文档

给予进阶用户指导的使用技巧

点击 [进阶文档](./level-2/) 以进行查看

::: tip 感谢
非常感谢大家无私分享使用技巧和心得, 使得 Xray 日益强大。
:::

---

---
url: /document/install.md
---
# 下载安装

## 平台支持

Xray 在以下平台中可用：

* Windows 7 及之后版本（x86 / amd64 / arm32 / arm64）；
  * Windows 7 中使用 1.8.4、1.8.6 的常规版本以及 1.8.18 以后的 `win7` 版本需要系统安装有 **KB4474419** 更新方可使用；推荐同时安装 KB4490628 以便联网后接受后续的操作系统安全更新。
  * 从 v25 开始，在 Windows 7 上运行 `win7` 版本只需安装 SP1 即可正常运行，但对联网系统而言仍然强烈建议安装后续操作系统安全更新。
* macOS 10.10 Yosemite 及之后版本（amd64 / arm64）；
* Linux 2.6.23 及之后版本（x86 / amd64 / arm / arm64 / mips64 / mips / ppc64 / s390x / riscv64）；
  * 包括但不限于 Debian 7 / 8、Ubuntu 12.04 / 14.04 及后续版本、CentOS 7 / 8、Arch Linux 等；
* FreeBSD (x86 / amd64)；
* OpenBSD (x86 / amd64)；

## 下载 Xray

预编译的二进制 ZIP 格式压缩包可在 [Github Releases](https://github.com/xtls/Xray-core/releases) 中找到。

下载对应平台的压缩包，解压后即可使用。

## 验证安装包

Xray 提供两种验证方式：

* ZIP 压缩包的 SHA1 / SHA256 摘要
* 可复现构建：请参照 [编译 Xray](../development/intro/compile.md)

## Windows 安装方式

* 在 [Github Releases](https://github.com/xtls/Xray-core/releases) 下载适用于 Windows 平台的 ZIP 压缩包，解压后可得到可执行文件 `xray.exe`
  ，然后[通过命令行带参数运行](./command) 即可
* 通过 [Scoop](https://scoop.sh) 包管理器安装：Xray 已经被添加到 [Mochi](https://github.com/Qv2ray/mochi)。

## macOS 安装方式

* 在 [Github Releases](https://github.com/xtls/Xray-core/releases) 下载适用于 macOS 平台的 ZIP 压缩包，解压后可得到可执行文件 `xray`
  ，然后[通过命令行带参数运行](./command.md) 即可
* 通过 [Homebrew](https://brew.sh) 包管理器安装：`brew install xray`
* [homebrew-xray](https://github.com/N4FA/homebrew-xray) 感谢[@N4FA](https://github.com/N4FA)

## Linux 安装方式

### 安装脚本

* Linux Script
  * [XTLS/Xray-install](https://github.com/XTLS/Xray-install) （**官方脚本**）
  * [tempest](https://github.com/team-cloudchaser/tempest) （支持 [`systemd`](https://systemd.io) 以及 [OpenRC](https://github.com/OpenRC/openrc); 仅限 Linux 下使用）

- One Click
  * [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
  * [Xray\_bash\_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool)
  * [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray\_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)

- Magisk
  * [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
  * [Xray\_For\_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)

### Arch Linux

#### Arch User Repository

需要使用 [AUR helpers](https://wiki.archlinux.org/index.php/AUR_helpers)，以 [yay](https://github.com/Jguer/yay)
为例，可通过 `yay -S xray` 安装。

#### Arch Linux CN

首先添加 [Arch Linux CN 仓库](https://www.archlinuxcn.org/archlinux-cn-repo-and-mirror/)，然后在 root 用户下使用 `pacman -S xray` 安装。

### Linuxbrew

Linuxbrew 包管理器的使用方式与 Homebrew 一致：`brew install xray`

### Debian&#x20;

### Gentoo

目前有三个第三方 Overlay 提供 Portage 安装脚本：

* [CHN-beta/touchfish-os](https://github.com/gentoo-mirror/touchfish-os/tree/master/net-proxy/Xray): 个人维护，适用于 systemD 系统
* [Gentoo-zh](https://github.com/microcai/gentoo-zh): 社区维护，适用于 systemD 系统
* [JuanCldCmt/Xray-Overlay](https://github.com/JuanCldCmt/Xray-Overlay)：个人维护，适用于 openRC 系统，同时使用 xray 用户组运行以提高安全性

使用 layman 或 eselect-repository 添加 Overlay 至本地，然后即可安装。

## Docker 安装方式

目前提供两种不同风格的 Docker 映像：

* [teddysun/xray](https://hub.docker.com/r/teddysun/xray) 有 root 权限、有 shell 环境、兼容所有 Alpine 支持的架构。由私人服务器 dl.lamp.sh 编译并构建。使用起来更便捷
* [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) 无 root 权限、无 shell 环境、支持更多的架构。由官方库编译并构建支持追溯。牺牲了便利性来追求更极端的安全性

### Docker image 的文件结构

teddysun/xray 版本映像：

* `/usr/bin/xray`：Xray 主程序
* `/etc/xray/config.json`：单一配置文件（其所在目录为挂载点）
* `/usr/share/xray/`：资源文件目录，存放了 v2fly 版本地理位置数据文件
  * geoip.dat
  * geosite.dat

ghcr.io/xtls/xray-core 版本映像：

* `/usr/local/bin/xray`：Xray 主程序（拥有者是 root:root、文件权限 755）
* `/usr/local/etc/xray/`：配置文件目录（挂载点）（拥有者是 root:root、目录权限 755、文件权限 644）
  * 00\_log.json
  * 01\_api.json
  * 02\_dns.json
  * 03\_routing.json
  * 04\_policy.json
  * 05\_inbounds.json
  * 06\_outbounds.json
  * 07\_transport.json
  * 08\_stats.json
* `/usr/local/share/xray/`：资源文件目录，存放了 Loyalsoldier 版本地理位置数据文件（权限同上）
  * geoip.dat
  * geosite.dat
* `/var/log/xray/`：日志文件目录（挂载点）（目录拥有者是 root:root、权限 755；文件拥有者是 65532:65532、权限 600）
  * access.log
  * error.log

# 图形化客户端

* OpenWrt
  * [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
  * [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
  * [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
* Asuswrt-Merlin
  * [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
* Windows
  * [v2rayA](https://github.com/v2rayA/v2rayA)
  * [v2rayN](https://github.com/2dust/v2rayN)
  * [Furious](https://github.com/LorenEteval/Furious)
  * [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
  * [AnyPortal](https://github.com/AnyPortal/AnyPortal)
* Android
  * [v2rayNG](https://github.com/2dust/v2rayNG)
  * [X-flutter](https://github.com/XTLS/X-flutter)
  * [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
  * [SimpleXray](https://github.com/lhear/SimpleXray)
  * [AnyPortal](https://github.com/AnyPortal/AnyPortal)
* iOS & macOS arm64 & tvOS
  * [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274))
  * [FoXray](https://apps.apple.com/app/foxray/id6448898396)
  * [Streisand](https://apps.apple.com/app/streisand/id6450534064)
* macOS arm64 & x64
  * [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
  * [v2rayA](https://github.com/v2rayA/v2rayA)
  * [v2rayN](https://github.com/2dust/v2rayN)
  * [V2rayU](https://github.com/yanue/V2rayU)
  * [V2RayXS](https://github.com/tzmax/V2RayXS)
  * [Furious](https://github.com/LorenEteval/Furious)
  * [OneXray](https://github.com/OneXray/OneXray)
  * [GoXRay](https://github.com/goxray/desktop)
  * [AnyPortal](https://github.com/AnyPortal/AnyPortal)
* Linux
  * [v2rayA](https://github.com/v2rayA/v2rayA)
  * [v2rayN](https://github.com/2dust/v2rayN)
  * [Furious](https://github.com/LorenEteval/Furious)
  * [GorzRay](https://github.com/ketetefid/GorzRay)
  * [GoXRay](https://github.com/goxray/desktop)
  * [AnyPortal](https://github.com/AnyPortal/AnyPortal)

## UUID 生成器

第三方的 UUID 生成器 [uuidgenerator.net](https://www.uuidgenerator.net)

---

---
url: /document/config.md
---
# 配置运行

[下载并安装](./install) 了 Xray 之后，您需要对它进行一下配置。

为了演示，这里只介绍简单的配置方式。更多的模板: [Xray-examples](https://github.com/XTLS/Xray-examples)

如需配置更复杂的功能，请参考更详细的 [配置文件](../config/) 中相关说明。

::: danger
为了避免你的流量被解密，
你应该使用 `xray uuid` 或 `uuidgen` 生成一个独一无二的uuid&#x20;
在服务端上，放入 `inbounds[0].settings.users[0].id` 内&#x20;
在客户端内，放入 `outbounds[0].settings.id` 内&#x20;
:::

## 服务端配置

你需要一台防火墙外的服务器，来运行服务器端的 Xray。配置如下：

```json
{
  "inbounds": [
    {
      "port": 10086, // 服务器监听端口
      "protocol": "vmess",
      "settings": {
        "users": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811" // 记得替换这个字段，使用 `xray uuid` 或 `uuidgen` 生成
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
```

服务器的配置中需要确保 `id` 和端口与客户端一致，就可以正常连接了。

## 客户端配置

在你的 PC（或手机）中，需要用以下配置运行 Xray ：

```json
{
  "inbounds": [
    {
      "port": 1080, // SOCKS 代理端口，在浏览器中需配置代理并指向这个端口
      "listen": "127.0.0.1",
      "protocol": "socks",
      "settings": {
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "address": "server", // 服务器地址，请修改为你自己的服务器 ip 或域名
        "port": 10086, // 服务器端口
        "id": "b831381d-6324-4d53-ad4f-8cda48b30811" // 记得替换这个字段，使用 `xray uuid` 或 `uuidgen` 生成
      }
    },
    {
      "protocol": "freedom",
      "tag": "direct"
    }
  ],
  "routing": {
    "domainStrategy": "IPOnDemand",
    "rules": [
      {
        "ip": ["geoip:private", "geoip:cn"], // 绕过局域网和国内IP段
        "outboundTag": "direct"
      }
    ]
  }
}
```

上述配置唯一要更改的地方是你的服务器 IP 和用户 uuid，配置中已注明。上述配置会把除局域网（比如访问路由器）和国内IP段（比如访问bilibili、acfun）以外的所有流量转发至你的服务器。

## 运行

* 在 Windows 和 macOS 中，配置文件通常是 Xray 同目录下的 `config.json` 文件。
  * 直接运行 `Xray` 或 `Xray.exe` 即可。
* 在 Linux 中，配置文件通常位于 `/etc/xray/` 或 `/usr/local/etc/xray/` 目录下。
  * 运行 `xray run -c /etc/xray/config.json`
  * 或使用 systemd 等工具将 Xray 作为服务在后台运行。

更多详细的说明可以参考 [配置文档](../config/) 和 [小小白话文](./level-0/)。

---

---
url: /document/command.md
---
# 命令参数

::: tip
Xray 使用 Go 风格的命令及参数
:::

## 获取基本命令

您可以运行 `xray help` 来获得所有 xray 最基础的用法, 以及可用的命令及说明。

```
Xray is a platform for building proxies.

Usage:

        xray <command> [arguments]

The commands are:

        run          Run Xray with config, the default command
        version      Show current version of Xray
        api          Call an API in an Xray process
        convert      Convert configs
        tls          TLS tools
        uuid         Generate UUIDv4 or UUIDv5 (VLESS)
        x25519       Generate key pair for X25519 key exchange (REALITY, VLESS Encryption)
        wg           Generate key pair for X25519 key exchange (WireGuard)
        mldsa65      Generate key pair for ML-DSA-65 post-quantum signature (REALITY)
        mlkem768     Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption)
        vlessenc     Generate decryption/encryption json pair (VLESS Encryption)

Use "xray help <command>" for more information about a command.
```

### xray run

指定一个或多个配置文件，并运行。

使用方法:

```
 xray run [-c config.json] [-confdir dir]
```

```
Run Xray with config, the default command.

The -config=file, -c=file flags set the config files for
Xray. Multiple assign is accepted.

The -confdir=dir flag sets a dir with multiple json config

The -format=json flag sets the format of config files.
Default "auto".

The -test flag tells Xray to test config files only,
without launching the server.

The -dump flag tells Xray to print the merged config.
```

`-config=` / `-c=` 用于指定使用的配置文件的位置，支持多文件配置。
`-confdir=` 用于指定一个包含多个配置文件的文件夹。
`-format=` 用于指定使用的配置文件的格式。
`-test` 用于测试配置文件的合法性。
`-dump` 用于显示多文件配置文件合并之后的效果。
::: tip
配置文件除了默认的 JSON 格式外，也可以使用 TOML 和 YAML。在不指定格式的前提下会通过文件扩展名识别。
:::

::: tip
当 `-config` 没有指定时，Xray 将先后尝试从以下路径加载 `config.json` :

* 工作目录（Working Directory）
* [环境变量](../config/features/env.md#资源文件路径)中 `Xray.location.asset` 所指定的路径
  :::

```
 xray run -dump
```

用以输出多文件配置融合之后的结果。

### xray version

输出 Xray 版本、 Golang 版本等信息。

使用方法:

```
 xray version
```

### xray api

调用 Xray 的 gRPC API，需要在配置文件中开启。

使用方法:

```
xray api <command> [arguments]
```

```
        restartlogger Restart the logger
        stats         Get statistics
        statsquery    Query statistics
        statssys      Get system statistics
        adi           Add inbounds
        ado           Add outbounds
        rmi           Remove inbounds
        rmo           Remove outbounds
```

### xray convert

把配置文件转换成 protobuf 或者把 typedMessage 转换成 JSON

使用方法：

```
xray convert <command> [arguments]

The commands are:

        pb           Convert multiple json configs to protobuf
        json         Convert typedMessage to json
```

`pb` 子命令使用示例：

```bash
# 用法：xray convert pb [-outpbfile out.pb] [-debug] [-type] [json file] [json file] ...

# 把三个配置合并成 mix.pb
xray convert pb -outpbfile mix.pb c1.json c2.json c3.json

# 使用 -debug 选项查看 mix.pb 的内容
xray convert pb -debug mix.pb

# 使用 mix.pb 启动 Xray-core
xray -c mix.pb

# 详细说明
xray help convert pb
```

json 子命令使用示例：

```bash
# 用法：xray convert json [-type] [stdin:] [typedMessage file]

tmsg='{
  "type": "xray.proxy.shadowsocks.Account",
  "value": "CgMxMTEQBg=="
}'

echo ${tmsg} | xray convert json stdin:

# 上面这个命令的输出结果是：
'{
  "cipherType": "AES_256_GCM",
  "password": "111"
}'

# 详细说明
xray help convert json
```

### xray tls

一些与 TLS 相关的工具。

使用方法:

```
xray tls <command> [arguments]
```

```
        cert          Generate TLS certificates
        ping          Ping the domain with TLS handshake
        certChainHash Calculate TLS certificates hash.
```

### xray uuid

生成 UUID。

使用方法:

```
xray uuid [-i "example"]
```

### xray x25519

生成 x25519 密钥对。

使用方法:

```
xray x25519 [-i "(base64.RawURLEncoding)" --std-encoding ]
```

### xray wg

生成 wireguard curve25519 密钥对。

使用方法:

```
xray wg [-i "(base64.StdEncoding)"]
```

::: tip
当 `-config` 没有指定时，Xray 将先后尝试从以下路径加载 `config.json` :

* 工作目录（Working Directory）
* [环境变量](../config/features/env.md#资源文件路径)中 `Xray.location.asset` 所指定的路径
  :::

### xray mldsa65

生成用于 REALITY 的 MLDSA-65 后量子签名密钥对。

使用方法:

```
xray mldsa65 [-i "seed (base64.StdEncoding)"]
```

### xray mlkem768

生成用于 VLESS Encryption 的 ML-KEM-768 后量子密钥交换用密钥对。

使用方法:

```
xray mlkem768 [-i "seed (base64.StdEncoding)"]
```

### xray vlessenc

生成可以直接用于 VLESS Encryption 的 encryption/decryption 选项内容。生成配置中 X25519 以及 ML-KEM-768 两种认证方式选一种使用即可，但是服务端及客户端必须采用同一种认证方式。临时密钥交换仍后量子安全，不受认证方式影响。

使用方法:

```
xray vlessenc
```

---

---
url: /document/document.md
---
# 为 Project X 的文档贡献

欢迎您为 Project X 的文档做出贡献，我们感谢每一位 Contributor 的贡献！是你们让 Xray 更加强大！

## 改进文档

Project X 的文档托管在 [GitHub](https://github.com/XTLS/Xray-docs-next) 上。

您可以通过以下步骤, 提交您对文档的改动：

1. 从 [Project X 文档仓库](https://github.com/XTLS/Xray-docs-next) 打开仓库，点击右上角的 fork, fork 一份文档仓库的镜像到您自己的 GitHub 仓库。

2. 对于简单的编辑您可直接在 GitHub 网页上完成；
   但复杂的编辑请一定使用 VSCode, 从您克隆的仓库获得文档的克隆，如：

   ```bash
   git clone https://github.com/你的GitHub用户名/Xray-docs-next.git
   ```

3. 基于 main 分支创建新的分支，如：

   ```bash
   git checkout -b your-branch
   ```

4. 在新分支上做修改。

   注：推荐使用自动格式 [prettier](https://prettier.io/docs/install)
   以及 [中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines)

5. 修改完成后，VSCode 插件会自动格式化您的更改，本仓库已经预先配置好了所有 VSCode 所需插件您只要根据 VSCode 提示一键安装即可。

   注：存在格式问题的 PR，将有可能被拒绝。

6. 提交修改，并推送到您的仓库中：

   ```bash
   git push -u origin your-branch
   ```

7. 打开 GitHub, 点击 'Pull request' 向 [project X 文档仓库](https://github.com/XTLS/Xray-docs-next) 提交 PR。

8. 请在 PR 的标题和正文中，概述此次 PR 新增/修改的内容等；

9. 等待回应, 如果 PR 被 merge, 您做的修改将直接呈现在 [Project X 文档网站](https://xtls.github.io)。

10. 想在本地预览完整效果？
    1. 安装 [Node.JS](https://nodejs.org/zh-cn/download)
    2. 安装 [pnpm](https://pnpm.io/zh/installation)
    3. 重启 VSCode 并打开本项目
    4. 按下 `Ctrl` + `` ` `` 打开 VSCode 集成终端
    5. 在终端运行命令

       ```bash
       pnpm install
       pnpm run docs:dev
       ```

## 发现问题？

如果您发现文档出错，可以改进文档或提交一个 Issue。

---

---
url: /document/level-0.md
---
# 小小白白话文

**这个章节是【从零开始】的基础课，新来的同学好好看好好学哦**

::: tip
Made with ❤️ by [@ricuhkaen](https://github.com/ricuhkaen)
:::

[【第 1 章】 前言罗嗦篇](./ch01-preface.md) - 机场还是自建？这是个问题

[【第 2 章】 原料准备篇](./ch02-preparation.md) - 工欲善其事，必先利其器

[【第 3 章】 远程登录篇](./ch03-ssh.md) - 一桥飞架南北，天堑变通途

[【第 4 章】 安全防护篇](./ch04-security.md) - 安全不注意，亲人两行泪

[【第 5 章】 网站建设篇](./ch05-webpage.md) - 秀出你的美

[【第 6 章】 证书管理篇](./ch06-certificates.md) - 领证的才是合法的

[【第 7 章】 Xray 服务器篇](./ch07-xray-server.md) - 终于等到你

[【第 8 章】 Xray 客户端篇](./ch08-xray-clients.md) - 新的开始

[【第 9 章】 附录](./ch09-appendix.md) - 考点都在这里

---

---
url: /document/level-0/ch01-preface.md
---
# 【第 1 章】 小小白白话文

## 1.1 这篇文档是写给谁的？

一句话：写给 **① 零基础** **② 希望学习自建 VPS** 的新人。

## 1.2 这篇文档不是写给谁的？

包括但不限于：各路大神大能、懒得自己折腾的小白、已经会折腾的高手、确定要用机场的土豪、确定要用一键脚本的逍遥派...... 总之只要有技术基础、或不愿不想自建的同学，您直接关闭本文即可，因为这篇文章大概是入不了您的法眼的，更可能会让您生一肚子闲气，那多划不来。

## 1.3 郑重声明及其他声明

郑重声明：

鄙人技术奇菜无比，故本文必然挂一漏万破绽百出。您若发现问题还请温柔提醒，莫要人参公鸡。

免责声明：

本文内容请您自行判断是否可信可靠可用，若您根据本文内容建立和使用 VPS 服务器时出了任何问题和不良结果，鄙人概不负责。

啰嗦声明：

基于本文【零基础用户】的目标受众，许多内容会尽力详尽说明，所以语言偏啰嗦，请做好心理准备。

## 1.4 为什么自建是个难题？

要回答这个问题，就需要稍微多说一点背景信息了。

一、科学上网这件事

科学上网这件事情，说来已经发展了近二十年（震惊!!!.jpg）。最初，自己稍微动动手即可（改改 host、连一下 ssh）、后来需要找一个网页代理，再后来需要写一个私有协议(比如 Shadowsocks)等等。

随着 GFW 技术这十几年来不断的迭代升级，若要完成【自己动手科学上网】这个目标，需要做的事情已经包括但不限于：

* 了解 Linux 系统基本命令
* 了解网络传输协议
* 有技术和经济能力完成 VPS 购买及管理
* 有技术和经济能力完成域名购买及管理
* 有技术能力完成 TLS 证书申请 等等。

这就让【自建 VPS 科学上网】这个曾经简单的行为逐渐变成了令新人望而生畏的挑战。

二、零基础用户的无奈

零基础的非技术用户，如果完成上面这一连串的操作，势必要学习大量的知识，但稍微搜索之后，新人只怕会更加迷茫：大量的信息散布在互联网的各个角落：博客、问答网站、群组、论坛、GitHub、Telegram、YouTube 等等等等）。这些信息纷乱复杂、水平良莠不齐、甚至可能互相矛盾。基本上就是不把新人彻底弄晕誓不罢休。

面对这些杂乱无章的信息，新人突然就从【信息匮乏】变成了【信息过剩】。若是几番连蒙带猜的折腾以失败告终（大概率如此）的话，他的积极性势必大受挫折。在这个过程中，若他又恰好去了一些不太友好的地方去求助，恐怕还要雪上加霜的被嘲讽一番：“这么菜，用机场不就行了，瞎折腾什么啊！”、“先去学会 Linux 再回来问吧”。

这时候，大概也只有一声“呵呵”可以表达心情了。

## 1.5 “用机场不就行了？”

首先，我想反问一下那些冷嘲热讽的人：“用机场”真的就是万灵药吗？

其次，我认为“不懂”和“不想懂”是有本质区别的。态度恶劣的巨婴伸手党自然惹人厌烦，但真心自学却不得要领的人不该受到无端的白眼和歧视，也正是这种对新人不加区分的恶劣社区氛围促使我写下本文。那么闲话少说，我们来看看机场的优势与劣势究竟如何：

一、“机场“的优势

所谓“机场”，就是“线路提供商”。他负责完成 1.4 提到的那一串技术操作和管理，用户则付费获得使用权。所以，它的优点至少有：

1. **用户操作简单**：扫码操作、一键添加规则等
2. **线路选择多**：可解锁不同国家、地区的网络服务；比如 iplc 等专线服务、游戏加速服务等
3. **接入节点多**：所以抵抗节点封锁的能力强一些，封了一个就换下一个

二、“机场”的风险

“方便”这枚硬币的另一面就是“风险”，基于“机场”的技术特点和市场情况，它的风险至少有：

1. **“机场”可完全获得用户信息**：用户在网上的所有痕迹，都【必然】经过且【非常可能】长期存储在其服务器上，这些记录无法受到任何具备法律效力的用户隐私协议的约束（**窥视、记录你的一举一动**）
2. **“机场”缺乏市场管理**：不可避免存在着以欺诈为目标的恶意商家（**主动跑路**）
3. **“机场”面临监管压力**：大机场相对有保障的同时，也无法避免树大招风。2020 年间，已经有几个大机场停运、跑路的事件发生，用户的正常使用受到严重干扰（**被动跑路**）
4. **“机场”技术水平难以确定**：线路质量良莠不齐，挂羊头卖狗肉的现象屡见不鲜（**速度慢、掉线多、连不上**）

## 1.6 那么你到底要不要自建呢？

现在，你已经看到了机场的优势和风险，要用什么，就请各位充分思考并自行决定。毕竟，最适合你的方案才是最好的方案。

![It's Your Choice!](./ch01-img01-choice.png)

1. 如果决定使用机场的话，现在，你可以关闭本文了。

2. 如果你决定自建，那就请继续阅读后面的章节吧！！

总之，本文的目标就是成为零基础用户的知识起点，提供对每一步充分的讲解和演示，清清楚楚（甚至**婆婆妈妈、絮絮叨叨、啰啰嗦嗦**）的协助新人完成【**从输入第一条命令开始，完成 VPS 服务器部署，并成功在客户端完成科学上网**】的全程。并在这个过程中帮助新人逐步接触和熟悉 Linux 的基础操作，为之后的进一步自学打下基础。

## 1.7 题外啰嗦几句

1. 墙外的信息泥沙俱下，请务必学会理性、独立的思辨，不要随意站队，不要轻信猎奇的信息。

2. 衷心希望大家获得更顺畅的网络后，可以获取更新鲜的知识、更丰富的娱乐、接触更美好的世界、结交更多志同道合的朋友，但不要成为任何有不可告人目的之人的替罪羊。

3. 你的互联网身份依然是你的身份，绝对的匿名化是极为困难的，所以请务必遵守你个人所在地区和 IP 所在地区的相关法律法规。无论何时，自我保护都是最基本的底线。

## 1.8 你的进度

> ⬛⬜⬜⬜⬜⬜⬜⬜ 12.5%

---

---
url: /document/level-0/ch02-preparation.md
---
# 【第 2 章】原料准备篇

这一章比较特殊，因为涉及到金钱交易行为，本文基于项目的中立立场，不做具体的推荐。我能做的，是告诉你需要准备哪些东西。

## 2.1 获取一台 VPS

你需要获取一台健康的、IP 没有被墙的 VPS，并在管理后台做下面这些基础准备：

1. 在 VPS 的后台安装 Debian系统
2. 小本本记下 VPS 的 IP 地址（本文会用 `"100.200.300.400"` 来表示）
   ::: tip
   这是一个故意写错的非法 IP，请替换成你的真实 IP）
   :::
3. 小本本记下 VPS 的 SSH 远程登陆端口(Port)
4. 小本本记下 SSH 远程登录的用户名和密码

购买 VPS 是一个比较复杂的事情，建议先去学习一下相关知识，选择适合自己的经济能力和线路需求的即可。另外可以选择薅一些国际大厂的羊毛（比如甲骨文和谷歌提供的永久免费或限时免费的套餐）。总之，务必量力而行。

::: tip 说明
关于选择 Debian 作为操作系统，这里稍微多说一句：不管你在网上听说了什么，不管哪个大神告诉你 XXX 版的 Linux 更好、XXX 版的 Linux 更牛，这些 Linux 的派系之争**跟现在的你半毛钱关系也没有**！使用最新的 Debian 稳定版足以让你的 VPS 服务器在安全、稳健运行的同时得到足够的优化（如 cloud 专用内核、及时的 bbr 支持等）。等你对 Linux 熟悉之后，再回头去尝试其他的 Linux 发行版也不迟。
:::

## 2.2 获取一个心仪的域名

你需要获取一个域名、并在 DNS 设置中添加一条 A 记录，指向你 VPS 的 IP 地址

1. 请选择靠谱的国际域名服务商。选择一些常见的域名后缀就行，注意不要用 `.cn` 后缀。
2. 在 DNS 设置中，添加一条指向你 VPS 的 IP 地址的 A 记录（A 记录的名字可以随便起，本文会用 `"a-name"` 来表示。完整的域名则会用 `"二级域名.你的域名.com"` 或者 `"a-name.yourdomain.com"` 来表示）。效果如下图：

![添加A记录](./ch02-img01-a-name.png)

::: tip
这**不是**一个真实可用的网址，请替换成你的真实网址
:::

## 2.3 你本地电脑上需要安装的软件

1. SSH 远程登录工具
   * Windows: [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)
   * macOS/Linux: Terminal

2. 远程文件拷贝工具
   * Windows: [WinSCP](https://winscp.net/eng/index.php)
   * macOS/Linux: Terminal

3. 靠谱的文本编辑器
   * Windows/macOS/Linux: [VSCode](https://code.visualstudio.com)

## 2.4 你的进度

如果上面的原材料你都准备好了的话，你已经拿到了开启新世界大门的钥匙。那还等什么，让我们快点进入下一章，走进这扇门吧！

> ⬛⬛⬜⬜⬜⬜⬜⬜ 25%

---

---
url: /document/level-0/ch03-ssh.md
---
# 【第 3 章】远程登录篇

## 3.1 远程登录 VPS (PuTTY)

首先，鉴于零基础人群中 Windows 的用户基数最大，所以本文以 Windows 为例进行展示。

其次，虽然 Windows 10 之后的 PowerShell 和 WSL 也可以达到很好的 SSH 操作体验。但是因为并非所有版本的 Windows 都有最新的组件，故本文还是以老牌的 PuTTY 为例，进行 SSH 远程登录的操作详解。（使用其他工具的话、在 SSH 登陆之后的操作都是一样的）

下面就跟我一步步操作吧。

1. 进入 PuTTY 的[官网](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)，选择适合你操作系统的版本下载。（本文以 64 位版本为例）

   ![下载PuTTY](./ch03-img01-putty-download.png)

2. 安装运行后，将会看到 PuTTY 的主界面。现在请拿出你上一章记东西的[小本本](./ch02-preparation.md#21-%E8%8E%B7%E5%8F%96%E4%B8%80%E5%8F%B0vps)，在下图的对应位置填入你 VPS 的**IP 地址(VPS IP)**和**端口(VPS PORT)**。为了方便以后使用时不用重复输入，我们可以保存会话 (Saved Sessions)，未来使用时只要按 Load 即可一键载入设置。

   ![设置PuTTY](./ch03-img02-putty-settings.png)

3. 我建议将 `Connection` 中的 `keepalive` 设置为 `60` 秒，防止你一段时间没有操作之后 SSH 自动断线。另外务必再次保存设置。

   ![防止频繁断线](./ch03-img03-putty-keepalive.png)

::: warning 注意
对 PuTTY 的任何设置更新都要再次手动保存 Session，不然关闭后就会丢失
:::

4. 点击 Open 就会进入 SSH 连接窗口，对应下图输入用户名与密码，与你的 VPS 远程主机建立连接。（本文假设默认用户名是 `root`，另外，在 Linux 系统输入密码的时候，是不会出现 `******` 这种提示符的，这样可以避免密码长度泄漏，不是你的键盘坏掉了哦！）

   ![SSH远程登录](./ch03-img04-ssh-login.png)

## 3.2 成功登录 SSH！初识命令行界面！

1. 如果你的信息都填写正确，你将会看到类似下图的界面，说明已登录成功：

   ![初次登录VPS](./ch03-img05-ssh-login-success.png)

   这个界面，就等于远程服务器的【桌面】，但它没有你熟悉的图标和鼠标，没有绚丽的色彩，有的只是简单文字，这就是【**命令行界面**】- `Command Line Interface`，或者缩写为 `CLI`。

   接下来的所有操作，都需要你像电影里的黑客一样，在这个命令行界面中完成。也许你会觉得陌生，但请相信我，使用命令行既不可怕，也不神秘。说到底，它只不过是把你习惯的鼠标操作变成了文字指令而已，**你说一句，它做一句**。

2. 现在，你可以稍微观察并熟悉一下命令行环境，这个界面其实已经告诉了你一些有用的信息了，比如系统内核版本（比如图内是 `4.19.37-5`）、上次登录时间及 IP 等。当然根据 VPS 的不同，你看到的界面可能会略有不同。

3. 请注意命令行最下面一行，闪动的光标左边，有一串字符。图中显示的是`root@vps-server:~#`，这一串要怎么理解呢？很简单：

   * 现在的用户是 `root`
   * `root` 所在的服务器是 `vps-server`
   * `root` 现在所在的文件夹是 `~`
   * `#` 之后是你可以输入命令的地方

   前两个很直观，无需多说。第三个是关于 Linux 的文件夹系统，现在也不需要过于深入，你只需要知道，"`~`"就是【当前用户的大本营】。第四个，提示符`#`，你也不用管，只需要知道，未来文章中会写一些需要你输入的命令，都会以 "`#`" 或者 "`$`" 开头，提示你**后面**是你输入命令的地方。（所以你复制命令的时候，**只需要复制后面的内容**，不要复制提示符）

## 3.3 第一次更新 Linux 的软件！

1. 正如你的手机，无论安卓还是 iPhone，为了 APP 及时更新（获取安全补丁和新功能），都会时不时从应用商店获得更新信息，并且提示你有多少个 APP 可更新。Linux 系统也有逻辑十分类似的更新机制。所以只要你会更新手机 APP，就能学会更新 Linux 软件！

2. Linux 下，每个 APP 都叫做一个“包” （package）。管理 APP 的程序自然就叫做“包管理器”（Package Manager）。你可以通过它安装、更新、卸载各种软件、甚至更新 Linux 系统本身。Linux 下的包管理器非常强大，此处按下不表，现在你只需要知道 Debian 系统的包管理器叫做 `apt` 即可。接下来，我们就先使用 `apt` 做一次软件的全面更新，让你熟悉它的基本操作。

3. 小小白白 Linux 基础命令：

   |   编号   |   命令名称    |   命令说明   |
   | :------: | :-----------: | :----------: |
   | `cmd-01` | `apt update`  | 查询软件更新 |
   | `cmd-02` | `apt upgrade` | 执行软件更新 |

4. 现在请输入第一条命令，获取更新信息

   ```shell
   apt update
   ```

5. 然后请输入第二条命令，并在询问是否继续安装 `(Y/n)` 时输入 `y` 并回车确认，开始安装

   ```shell
   apt upgrade
   ```

6. 完整流程演示如下：

   ![初次软件更新流程演示](./ch03-img06-apt-upgrade-full.gif)

## 3.4 你的进度

**恭喜你又迈出了坚实的一步！** 现在，你已经可以通过 SSH 来登录你的远程服务器了！那登录进去之后，除了升级软件之外，应该再做点什么呢？敬请进入下一章一探究竟吧！

> ⬛⬛⬛⬜⬜⬜⬜⬜ 37.5%

---

---
url: /document/level-0/ch04-security.md
---
# 【第 4 章】安全防护篇

## 4.1 为什么要做安全防护

Linux 服务器的安全防护是一个纷繁复杂的巨大课题。无数的网站、APP、服务、甚至线下基础设施都建立在 Linux 的基石之上，这背后牵涉到巨大的经济利益和商业价值，当然也就就意味着黑灰产有巨大的攻击动力。但是这些服务是如此重要、根本不允许出现重大的安全漏洞。于是无数的运维专业人员都在安全攻防的战场上拼搏努力，这才让大家能享受到基本稳定的现代化数字生活。

现在，你拥有了一台 VPS，并且将会敞开他的数据访问渠道来达到流量转发的目标，那就相当于你已经置身于安全攻防战场的第一线、直面所有风险。但与此同时，新人由于知识和信息的不足，看待安全问题是总是难免两极分化：要么觉得轻如鸿毛和自己没有半点关系，要么觉得重于泰山甚至惶惶不可终日。

* 对于前者，我的建议是：安全无小事，尽量多查一些安全方面的信息，免得自己真的受了损失才后悔莫及

* 对于后者，我的建议是：不用紧张，我们的服务器仍不具有太高的价值、一般不会吸引到高水平的攻击，需要面对的基本都是一些自动化脚本的恶意扫描和登录尝试，跟着本文做一些基础的防护即可

## 4.2 具体的风险到底是什么

就像我们在《远程登录篇》配置的一样，任何人只需要知道【IP 地址】+【端口】+【用户名】+【密码】这四个要素，就能登录你的 VPS 服务器。那很显然，这四要素的安全就是我们要防护的底线。我们来逐一分析：

1. 【IP 地址】：恶意脚本会随机尝试和扫描 IP 段，可以简单认为是公开信息、无法隐藏

2. 【端口】：如果使用默认端口，那么【端口 = `22`】

3. 【用户名】：如果使用默认用户，那么【用户名 = `root`】

4. 【密码】：密码不存在默认值，一定是由 VPS 后台随机生成或由你自行设置的。也就是说，如果你的服务器都是默认设置，则四要素中的三个已经是已知的，那么你整个服务器的安全，就全部寄托在一串小小的密码上了。这时有几种情况：
   * 如果你用了 VPS 管理后台随机生成密码，它一般包含随机的十几个大小写混杂的字母和符号，相对比较安全

   * 如果你为了好记、把密码改成了类似`123456`这种超弱的密码，破解你的 VPS 服务器可谓不费吹灰之力

   * 如果你为了好记、把密码改成了比较复杂、但在别的地方用过的密码，其实也并不安全。你要明白黑客手里有作弊器，比如说`密码表`，包含数万、数十万、数百万甚至更多曾经泄漏的真实密码）

5. 但你要明白，没有哪个黑客真的要坐在电脑前一次一次的尝试你的密码，全部的攻击尝试都是恶意脚本自动进行的，它会 24 小时不眠不休的工作。也许每天你酣睡之时，你的服务器都在经受着一轮又一轮的冲击。

   一旦密码被成功撞破，意味着你的四要素全部被攻击者掌握，恶意脚本就会快速登录服务器、获取服务器的最高 `root` 控制权、安装部署它的恶意服务，然后就可以用你的服务器来 24 小时做各种坏事（比如挖矿、传播病毒、发送垃圾邮件、欺诈邮件、做 BT 中继、甚至暗网公众节点等等等等）。如果恶意脚本比较克制，其实可以做到相当的隐蔽性。而新人一般也不会去观察留意 VPS 的登录记录、进程变化、CPU 占用变化、流量变化等指标，你其实就很难发现自己被黑了。直到你的 VPS 服务商封禁你的账号、或者收到律师函为止。

6. 别忘了，你获得 VPS 时大概率需要使用真实的支付信息，你登录各种网站、社交平台时也会留下你的 IP 地址，这些都与你的身份有直接或者间接的关系。于是，**一旦这些坏事发生，它们就不可避免的与你产生了关联。**

## 4.3 我们要做的安全防护有哪些

基于上述分析，我们要做的，自然就是对【端口】、【用户名】、【密码】这三要素进行加强，来降低被攻破的风险：

1. 【端口】：将 SSH 远程登录端口修改为【非 22 端口】 （4.4）
2. 【用户名】：建立【非 root】的新用户、并禁用 root 用户 SSH 远程登录 （4.5、4.6）
3. 【密码】：SSH 启用 RSA 密钥验证登录、同时禁用密码验证登录 （4.7）

记得按顺序来，别把自己锁在门外了。

## 4.4 将 SSH 远程登录端口修改为非 22 端口

现在，我们来解决【端口 = `22`】的问题。（注意：有些 VPS 服务商，默认的端口已经是非 22 端口，那么你可以忽略这一步，当然也可以跟着本文改成别的端口）

1. 小小白白 Linux 基础命令：

   |   编号   |      命令名称       |   命令说明   |
   | :------: | :-----------------: | :----------: |
   | `cmd-03` |       `nano`        |  文本编辑器  |
   | `cmd-04` | `systemctl restart` | 重启某个服务 |

2. 小小白白 Linux 基础配置文件

   |   编号    |      配置文件位置      |       文件说明       |
   | :-------: | :--------------------: | :------------------: |
   | `conf-01` | `/etc/ssh/sshd_config` | SSH 远程登录程序设置 |

3. 我们要做的第一件事，当然就是【用`nano`这个文本编辑器打开`SSH远程登录程序设置`】，在 Windows 下，你会【找到文件并双击】，在 Linux 下该怎么办呢？仔细看看上面的命令说明，是不是就很简单了？没错，就是：

   ```shell
   nano /etc/ssh/sshd_config
   ```

4. 文件打开后，你就进入了`nano`的界面，稍微观察一下，你会发现，它把重要的快捷键都显示在屏幕下方了（下图红框内），直接开卷考试、不用死记硬背，是不是很贴心呢？

   ![nano的界面](./ch04-img01-nano-ui.png)

5) 我们要做的第二件事，是【在打开的文件中找到`Port`这一项，并修改它的端口】。Port 后面的数字就是 SSH 的端口，一般建议把它改成一个大于`1024`小于`65535`的整数（本文以`9753`为例）。请结合`nano`的快捷键，想一下该怎么操作呢？果然，你又说对了！就是：

   * 使用 `ctrl+w` 进入搜索模式，然后输入 `Port 22` 并回车
   * 删除 `22` 并改成 `9753`
   * 说明：如果这一行开头有个`#`，证明这一行【不生效】（被注释掉了），你可像我一样在文件最后写一个不带`#`的，或者把`#`删掉就好。

   ::: warning
   本文以`9753`为例，就意味着随着本文的发布，这个端口会变成一个不大不小的特征，也许会被攻击者优先尝试、也许被 GFW 干扰、阻断。所以我强烈建议你用一个自己想到的其他端口，毕竟，你有 6 万多个端口可以自由选择。
   :::

6. 我们要做的第三件事，是【保存文件并退出】

   * 如果第 3 步你有仔细观察，就会发现保存并不是常见的 `ctrl+s`。
   * 正确的快捷键：保存是 `ctrl+o` + `回车`，退出是 `ctrl+x`
   * (部分操作系统) 新增一个防火墙规则，设置为新增的SSH端口, 否则实例重启后无法SSH登陆。
   * 如 Ubuntu 的 ufw

   ```shell
   sudo ufw allow 9753/tcp
   ```

7. 我们最后要做的事，是【重启 ssh 服务，使变更生效】

   ```shell
   systemctl restart ssh
   ```

   然后可以尝试在ssh软件上打开新的会话尝试是否可以连上，如果出现问题可以通过旧的ssh会话修改配置(重启sshd时已经打开的ssh不会被关闭)

8. 完整流程演示如下：

   ![修改非22端口演示](./ch04-img02-sshd-conf-full.gif)

9. 修改 PuTTY 配置

   现在新的端口已经生效，下次使用 PuTTY 登录时就要用`9753`了。所以现在请到 PuTTY 的设置中修改端口号码，然后保存 Session。嗯，你应该知道去哪里改了吧？（如果不知道的话，要重读前面的内容了哦！）

## 4.5 建立非 root 的新用户

第二步，我们来解决【用户名 = `root`】的问题。

首先你要理解， Linux 系统中的`root`，不仅仅是一个管理员账号那么简单。它是整个系统的【根基】、是系统的主宰、至高无上的神。一旦`root`账号出现安全问题，整个系统都只能任人鱼肉、无处可逃。那么就跟随我进行操作吧：

1. 小小白白 Linux 基础命令：

   |   编号   |   命令名称    |           命令说明           |
   | :------: | :-----------: | :--------------------------: |
   | `cmd-05` |   `adduser`   |        给系统新增用户        |
   | `cmd-06` | `apt install` |         安装某个软件         |
   | `cmd-07` |   `visudo`    | 修改 sudo 权限设置专用编辑器 |

2. 我们要做的第一件事，是【新增一个用户并设定登录密码】，名字你可以随便起，我这里以`vpsadmin`为例：

   ```shell
   adduser vpsadmin
   ```

   执行命令后，根据提示操作即可。请务必设置一个用户密码（别忘记设置密码时你时看不到 `******` 的）。之后系统会询问你一些用户的附加信息，这些就可以无视，一路回车即可。

   ![建立新用户](./ch04-img03-adduser.png)

   ::: warning
   本文以`vpsadmin`为例，就意味着随着本文的发布，这个用户名也会变成一个不大不小的特征，也许会被攻击者优先尝试。所以和端口一样，我强烈建议你用一个自己想到的其他用户名。
   :::

3. 完整流程演示如下：

   ![建立新用户](./ch04-img04-adduser-full.gif)

4. 我们要做的第二件事，是【安装`sudo`功能】（`sudo` 就是在关键时刻，让普通账户临时获得 `root` 的神力，战力全开拯救世界）

   ```shell
   apt update && apt install sudo
   ```

   聪明的你大概已经发现，这一行命令其实是两个命令。前一半 `apt update` 你之前已经见过并且用过了，是去服务器刷新软件版本信息。后面的 `apt install`
   就是这一次要用到的【安装命令】。两条连接在一起，就是让系统去【刷新可用的最新软件，然后安装最新版的`sudo`程序】。 `&&` 则是把两个命令连起来执行的意思。

5. 我们要做的第三件事，是【把`vpsadmin`用户加入`sudo`名单里，让他有资格借用`root`的神力】

   ```shell
   visudo
   ```

   在 `User Privilege Specification` 下加入一行 `vpsadmin ALL=(ALL) NOPASSWD: ALL` 即可。

   ::: warning
   我要特别说明的是`NOPASSWD`这个设置，它的意思是`vpsadmin`用户临时使用`root`权限时，不用额外输入密码。**这与一般的安全建议相反**。我之所以如此推荐，是因为很多新人不顾危险坚持使用`root`账号就是因为用`root`时不用重复输入密码、觉得轻松。“两害相权取其轻”，我认为【直接用`root`用户的风险】大于【使用`sudo`
   时不用输密码的风险】，所以做了以上的建议。

   如果你希望遵守传统习惯、每次使用`sudo`时需要输入密码，那么这一行改成 `vpsadmin ALL=(ALL:ALL) ALL` 即可。
   :::

6. 完整流程演示如下：

   ![建立新用户](./ch04-img05-sudo-full.gif)

## 4.6 禁用 root 用户 SSH 远程登录

1. 现在你已经逐渐熟悉 Linux 了，所以这次换你思考，我们要做的第一件事是什么呢？没错，还是【用`nano`编辑器打开`SSH远程登录程序设置`】，什么，你想不起来怎么操作了？那去复习一下上面的内容再回来吧！............ 正确答案：

   ```shell
   nano /etc/ssh/sshd_config
   ```

2. 找到`PermitRootLogin Yes`这一项，然后把它后面的设定值改为`no`即可。还记得怎么操作吗？............ 正确答案：
   * 使用 `ctrl+w` 进入搜索模式，然后输入 `PermitRootLogin` 并回车
   * 删除 `yes` 并改成 `no`

3. 保存文件并退出。还记得怎么操作吗？............ 正确答案：
   * 保存是 `ctrl+o`，然后 `回车` 确认
   * 退出是 `ctrl+x`

4. 重启 ssh 服务，让变更生效。还记得............ 算了直接公布正确答案：

   ```shell
   systemctl restart ssh
   ```

5. 完整流程演示如下：

   ![禁用root用户SSH远程登录](./ch04-img06-ssh-no-root-full.gif)

6. 下次通过 PuTTY 远程 SSH 登录的时候，`root`用户已无法连接，用户名就要换成`vpsadmin`了！方便起见，我们可以在 PuTTY 中把`vpsadmin`设置成默认登录用户名。（啰嗦君：别忘了保存 Session）

   ![PuTTY设置默认用户名](./ch04-img07-putty-default-user.png)

## 4.7 使用 RSA 密钥登录并禁用密码登录

第三步，我们来解决【密码】可能被撞破的问题。

前面我说过，黑客并不是很蠢的用穷举法破解你的密码，而是会用一些比如“密码表”的作弊手段。除非你用的是随机生成的超长密码（比如借助 1Password，或者 macOS 的 keychain 等密码管理工具），否则很容易中招。

超长随机密码虽然安全性有所提高，但是基本上无法记忆，手动输入也十分麻烦易错。为了解决这个困境，我们可以直接弃用【密码验证】方式，改用更安全的【密钥验证】。

所谓的【密钥验证】，就是生成【一对】相关联的密钥文件（公钥和私钥），然后把【公钥】上传到 VPS 备用。每次登录时，SSH 会将【公钥】和【私钥】进行匹配，若验证是正确的【密钥对】，则验证通过。（换言之，你无需记忆和输入复杂的密码，只要保护好【私钥】这个文件不外泄即可）

::: warning
本文以 `RSA` 密钥举例，是因为 `RSA` 密钥在各种设备、各种 `SSH` 客户端中有广泛悠久的支持历史，且目前依然能提供够用的安全性。但它绝非唯一选择。

其他的常见密钥还有：

* `DSA` - 已经从数学层面被证明不安全，所以永远不要用它
* `ECDSA` - 密钥小安全性高，但其算法被指留有 NSA 的后门，如果你的 VPS 上有值得 NSA 关注的东西就不要用它
* `Ed25519` - 这是一个与 `ECDSA` 十分类似的算法，故具有相似的性能优势。同时其文档全部公开，所以普遍认为无后门

所以，如果你的设备和软件都支持的话，我建议优先选择 `Ed25519` 密钥。
:::

那我们现在就来配置【密钥验证】吧！

1. 运行`PuTTYgen` (PuTTY 密钥生成器)。位置是 `开始菜单` --> `所有程序` --> `PuTTY (64-bit)` --> `PuTTYgen`

   1. 点击`Generate`开始生成（在界面空白处乱晃鼠标增加随机数）

      ![生成密钥](./ch04-img08-puttygen-save.png)

   ::: warning
   本图中是以 `2048` 位的 `RSA` 密钥为例的。但实际上，如果要获得与 `EDCSA/Ed25519` 的 `256` 位密钥相同的安全性，你需要使用 `3072` 位的 `RSA` 密钥。（即右下角的数字改成 `3072`）
   ::: 2. 你可以给私钥设置密码，增加一层安全性 3. 点击 `Save public key` 保存公钥，文件名为 `id_rsa.pub` 4. 点击 `Save private key` 保存私钥，文件名为 `id_rsa` (PuTTY 私钥自带`.ppk`后缀) 5. 最重要的，将上方红框内的内容，向下滚动全部复制出来并保存，文件名为 `authorized_keys`。（用 vscode 保存，默认会变成带`txt`后缀的文本文件，这没关系，之后上传 VPS 时我们会把后缀名去掉）

   ![保存密钥](./ch04-img09-puttygen-save-keys.png)

2. 将公钥上传至 VPS 的`vpsadmin`用户下
   1. 这一步就需要用到之前准备的`WinSCP`了。

   2. 去[官网](https://winscp.net/eng/index.php)下载并安装，会提示你导入 PuTTY 的设置，当然一键导入啦！

      ![一键导入Session](./ch04-img10-winscp-import-session.png)

   3. 如果没有提示导入或者你已经提前安装好了，那按照下图进行配置即可

      ![WinSCP登录设置](./ch04-img11-winscp-ui.png)

   4. WinSCP 左边的目录就是本地电脑上的文件夹和文件，请定位到密钥所在的文件夹

   5. WinSCP 右边的目录则是 VPS 服务器上的文件夹和文件，默认就在 `/home/vpsadmin/` 文件夹，此时在请点击右下角 `X hidden` 来显示隐藏文件

      ![本地和远程文件夹](./ch04-img12-winscp-locations.png)

   6. 在右边（VPS 中）点击右键并新建文件夹，起名`.ssh` （注意有一个`.`）

      ![在VPS中建立放置公钥的文件夹](./ch04-img13-winscp-newfolder-key.png)

   7. 将【公钥】`authorized_keys`上传到`.ssh`文件夹内

      ![上传authorized\_keys](./ch04-img14-winscp-upload-key.png)

   8. 在上传时，将【公钥】从 `authorized_keys.txt` 改名为 `authorized_keys`（去掉`.txt`这个后缀名）

      ![确保没有任何后缀](./ch04-img15-winscp-rename-key.png)

   9. 完整流程演示如下：

      ![WinSCP操作完整演示](./ch04-img16-winscp-full.gif)

3. 在 VPS 端设置 SSH 启用 RSA 密钥验证登录、同时禁用密码验证登录
   1. 小小白白 Linux 基础命令：
      | 编号 | 命令名称 | 命令说明 |
      |:--:|:--:|:--:|
      | `cmd-08` | `sudo` | 用`root`权限运行某个命令 |
      | `cmd-09` | `chmod` | 修改目标文件/文件夹的权限 |

   2. SSH 远程连接到 VPS 上（PuTTY）

   3. 修改 `authorized_keys` 文件权限为 `600` （仅所有者可读可写）

      ```shell
      chmod 600 ~/.ssh/authorized_keys
      ```

   4. 修改 SSH 配置。这个我们已经用了很多次，但现在我们已经从无所不能的`root`变成了普通用户`vpsadmin`，此时的我们是没有权限直接编辑 SSH 配置的。这时候就需要使用`sudo`命令了：

      ```shell
      sudo nano /etc/ssh/sshd_config
      ```

   5. 找到(`ctrl+w`) `PasswordAuthentication` 改成 `no`

   6. 找到(`ctrl+w`) `PubkeyAuthentication` 改成 `yes`，然后保存(`ctrl+o`)退出(`ctrl+x`)

   7. 重启 SSH 服务。（啰嗦君：别忘了现在需要使用`sudo`来获得权限）

      ```shell
      sudo systemctl restart ssh
      ```

   8. 完整流程如下:

      ![SSH开启密钥验证并禁用密码验证](./ch04-img17-rsa-login-full.gif)

4. VPS 端已经设置好了公钥，现在要给 PuTTY 指定私钥位置供登录时使用（啰嗦君：别忘了保存 Session）

   ![PuTTY指定私钥位置](./ch04-img18-putty-privatekey-location.png)

5. 至此，【密钥登录】已成功开启、【密码验证】已成功关闭、并且还给 PuTTY 保存了默认的登录用户名和私钥。未来使用 PuTTY 登录时，载入`VPS-SERVER`配置后，点击`Open`就可以一键登录了。

   如果你给私钥设置了密码保护，登录时当然还需要输入这个密码才能使用密钥，如下图：

   ![输入私钥密码](./ch04-img19-putty-privatekey-passphrase.png)

6. 别忘了给`WinSCP`也做对应的密钥设置，否则之后想要传输文件时就无法登录了：

   ![WinSCP指定私钥位置](./ch04-img20-winscp-privatekey-location.png)

::: warning
任何需要借助 SSH 进行登录的软件都需要密钥验证了，软件过多，无法逐一展示，请根据你的需要自行设置好哦
:::

## 4.8 你的进度

到这里为止，你的 VPS 已经完成了【端口】、【用户名】、【密码】这三要素的基本安全保障，虽然远称不上固若金汤，但一般的恶意脚本应该已经无法对你造成伤害了！

现在我们终于有了一个安全的系统基础，下一章，我们就可以开始逐步安装配置 Xray 需要的基础设施了！（什么基础设施呢？一个网页，一张证书）

> ⬛⬛⬛⬛⬜⬜⬜⬜ 50%

---

---
url: /document/level-0/ch05-webpage.md
---
# 【第 5 章】网站建设篇

## 5.1 为什么要做一个网站？

新人也许会迷惑，为什么科学上网还要建一个网站？我不会编程啊，是不是特别麻烦？

先回答第一个问题，建网站的原因有：

1. 申请合法的 TLS 证书（非常重要）
2. 提供合理的回落，防止主动探测攻击，提高安全性
3. 建设一个伪装站（如博客、私人网盘、多媒体网站、游戏网站等），直接访问时有合理的前台，使流量使用看上去更合理。

再回答第二个问题：

1. 本文作为演示，仅仅使用了一个最简单的【单文件 html 页面 + Nginx】来搭建，以此完成上面的目标，所以【非常简单】
2. 这个网站完全可以不仅仅是伪装，而是真的做大做强，这个复杂性就完全取决于你了
3. 对于“伪装”和“网站运营”这个目标，需要的就是各不相同、秀出真我，需要的同学可以自行搜索学习。这个内容已经完全偏离了科学上网，本文就不深入解析了。

## 5.2 登录 VPS、安装运行 Nginx

1. 这里用到的，都是之前已经详解过的命令，所以就不重复讲解了。看不懂的同学可以看看前面的章节哦。

   ```shell
   sudo apt update && sudo apt install nginx
   ```

2. 完成后，Nginx 已经自动运行。此时打开 Windows 上的浏览器并输入 `http://100.200.300.400:80`，若看到下图的界面就说明 Nginx 已经正常在运行了。

   ![Nginx默认界面](./ch05-img01-nginx-default-running.png)

3. 如果无法看到上述Nginx默认页面，可能是需要配置Debian系统上默认的防火墙组件Uncomplicated Firewall (UFW)，以便启用 HTTP (80) 和 HTTPS (443) 端口流量。

   a. 验证方法，输入：

   ```shell
   sudo ufw status
   ```

   b. 如果输出如下，表明80和443端口未开启，需要执行c步骤

   ```shell
   Status: active
   To                         Action      From
   --                         ------      ----
   22/tcp                     ALLOW       Anywhere
   22/tcp (v6)                ALLOW       Anywhere (v6)
   ```

   c. 启用UFW的Nginx 80 和 443 端口命令

   ```shell
   sudo ufw allow 'Nginx Full'
   ```

   d. 输入a中命令再次验证，如果输出如下，表示Nginx流量已经被防火墙放行，这样就应该可以看到前述第2点中的Nginx默认页面。

   ```shell
   Status: active
   To                         Action      From
   --                         ------      ----
   22/tcp                     ALLOW       Anywhere
   Nginx Full                 ALLOW       Anywhere
   22/tcp (v6)                ALLOW       Anywhere (v6)
   Nginx Full (v6)            ALLOW       Anywhere (v6)
   ```

## 5.3 创建一个最简单的网页

1. 小小白白 Linux 基础命令：
   | 编号 | 命令名称 | 命令说明 |
   |:--:|:--:|:--:|
   | `cmd-10` | `mkdir` | 新建文件夹 |
   | `cmd-11` | `systemctl reload` | 重新加载某个服务 |

2. 小小白白 Linux 基础配置文件：
   | 编号 | 配置文件位置 | 文件说明 |
   |:--:|:--:|:--:|
   | `conf-02` | `/etc/nginx/nginx.conf` | Nginx 程序设置 |

3. 创建一个网站专用的文件夹`/home/vpsadmin/www/webpage/`并建立网页文件`index.html`
   ```shell
   mkdir -p ~/www/webpage/ && nano ~/www/webpage/index.html
   ```

::: warning
如果你用的不是 `vpsadmin` 这个用户名，请务必理解这条命令中 `“~”` 符号的意义（这关系到【第 5 步】你要写的内容）：

* 如果是 【非 `root` 用户】，`“~”` 就等价于 `/home/用户名`
* 如果是 【 `root` 用户】，`“~”` 就等价于 `/root`
  :::

4. 把下面的内容完整的复制进去，然后保存(`ctrl+o`)退出(`ctrl+x`)

   ```html
   <html lang="">
     <!-- Text between angle brackets is an HTML tag and is not displayed.
           Most tags, such as the HTML and /HTML tags that surround the contents of
           a page, come in pairs; some tags, like HR, for a horizontal rule, stand
           alone. Comments, such as the text you're reading, are not displayed when
           the Web page is shown. The information between the HEAD and /HEAD tags is
           not displayed. The information between the BODY and /BODY tags is displayed.-->
     <head>
       <title>Enter a title, displayed at the top of the window.</title>
     </head>
     <!-- The information between the BODY and /BODY tags is displayed.-->
     <body>
       <h1>Enter the main heading, usually the same as the title.</h1>
       <p>
         Be <b>bold</b> in stating your key points. Put them in a list:
       </p>
       <ul>
         <li>The first item in your list</li>
         <li>The second item; <i>italicize</i> key words</li>
       </ul>
       <p>Improve your image by including an image.</p>
       <p>
         <img
           src="https://i.imgur.com/SEBww.jpg"
           alt="A Great HTML Resource"
         />
       </p>
       <p>
         Add a link to your favorite
         <a href="https://www.dummies.com/">Web site</a>. Break up your
         page with a horizontal rule or two.
       </p>
       <hr />
       <p>
         Finally, link to <a href="page2.html">another page</a> in your own
         Web site.
       </p>
       <!-- And add a copyright notice.-->
       <p>&#169; Wiley Publishing, 2011</p>
     </body>
   </html>
   ```

   赋予其他用户读取该文件的权限

   ```shell
   chmod -R a+r .
   ```

5. 修改 `nginx.conf` 并重启 `Nginx` 服务，将`80`端口的 http 访问定位到刚才建立的 `html` 页面上
   1. 修改 `nginx.conf` 。

      ```shell
      sudo nano /etc/nginx/nginx.conf
      ```

   2. 将下面一段，添加在 `http{}` 内，然后保存(`ctrl+o`)退出(`ctrl+x`)。（记得将域名替换为之前准备好的、包含二级域名的真实域名）

      ```
              server {
                      listen 80;
                      server_name 二级域名.你的域名.com;
                      root /home/vpsadmin/www/webpage;
                      index index.html;
              }
      ```

      ::: warning 特别注意！
      如我在【第 3 步】中的提示所说，请务必确保 `/home/vpsadmin/www/webpage` 改成你的实际文件路径。
      :::

   3. 让 `nginx` 重新载入配置使其生效

      ```shell
      sudo systemctl reload nginx
      ```

   4. 完整的设置流程如下：

      ![网页设置演示](./ch05-img02-nginx-conf-full.gif)

   5. 此时如果你访问 `http://二级域名.你的域名.com`，你看到这样的页面则说明成功：

      ![http网页成功](./ch05-img03-nginx-http-running.png)

## 5.4 常见错误的说明

首先，如果你一路按照文章的说明来操作，并且足够细心，那肯定不会出错。所以，我并不打算修改本文的写法。

那为什么依然有很多同学卡在了这一步，网页怎么也打不开呢？基本上就是两个字：**粗心**。因为这里配置可能出现的问题只有两种，原因也只有两个。

一、两种问题：

* `nginx.conf` 里面的 `/home/vpsadmin/www/webpage` 这一条，与你的实际文件路径不符，`nginx` 找不到文件
* 路径正确，但 `nginx` 无权读取

二、两个原因：

* 使用了【非 `root` 用户】，但仍然直接拷贝文中的命令不加修改。（这基本就等于抄答案时把同学的名字一起抄过去了）
* 坚持使用【 `root` 用户】

碰到错误的同学，就回过头仔细看一下【5.3】中【第 3 步】和【第 5-2 步】的说明吧。

::: warning
本文前期已经用了大量篇幅说明了使用【非 `root` 用户】对安全的重要性，全文也是基于此而写。所以，因使用【 `root` 用户】而导致的问题并不在本文的设计范围里。

但我相信，坚持使用【 `root` 用户】的同学应该是有主见、动手能力强、或者有一定 Linux 基础的同学。问题的症结我已经全部说明了，我相信你一定可以自行解决。
:::

## 5.5 你的进度

至此，Xray 的第一个基础设施【网页】已经就位，我们马上就进入第二个基础设施【证书】吧！

> ⬛⬛⬛⬛⬛⬜⬜⬜ 62.5%

---

---
url: /document/level-0/ch06-certificates.md
---
# 【第 6 章】证书管理篇

## 6.1 申请 TLS 证书

接下来我们要做的，是为我们的域名申请一个真实的 TLS 证书，使网站具备标准 TLS 加密的能力及 HTTPS 访问的能力。这就是 Xray 等现阶段安全代理工具确保流量充分加密最重要的工具。

::: warning
请不要轻易使用自签证书。它并没有让操作简单太多，但增加了无谓的风险（如中间人攻击）。
:::

这里我会使用一个叫做 [`acme.sh`](https://github.com/acmesh-official/acme.sh) 的证书管理工具，它简单、轻量、高效，并可完成证书自动更新。

另外，我相信，现在你已经逐渐熟悉了 Linux 的基础操作，所以已经多次出现的命令从本章开始不再重复截图、只做简单的描述。如果实在想不起来怎么用的话，就稍微复习一下前面的章节吧。

## 6.2 安装 `acme.sh`

1. 小小白白 Linux 基础命令：
   | 编号 | 命令名称 | 命令说明 |
   |:--:|:--:|:--:|
   | `cmd-12` | `wget` | 访问（或下载）某个网页文件 |
   | `cmd-13` | `acme.sh` | acme.sh 证书管理相关的命令 |

2. 运行安装脚本

   ```shell
   wget -O -  https://get.acme.sh | sh
   ```

3. 让 `acme.sh` 命令生效

   ```shell
   . .bashrc
   ```

4. 开启 `acme.sh` 的自动升级

   ```shell
   acme.sh --upgrade --auto-upgrade
   ```

5. 到这一步的完整流程如下图：

   ![acme.sh安装演示](./ch06-img01-acme-install.gif)

## 6.3 测试证书申请

在正式申请证书之前，我们先用测试命令(`--issue --server letsencrypt_test`)来验证是否可以成功申请，这样可以避免在本地配置有误时，反复申请证书失败，超过 Let's Encrypt 的频率上限（比如，每小时、每个域名、每个用户失败最多 5 次），导致后面的步骤无法进行。

1. 测试证书申请的命令如下（本文均以 `ECC` 证书为例，因为时至今日，实在没什么理由不用它）：

   ```shell
   acme.sh --issue --server letsencrypt_test -d 二级域名.你的域名.com -w /home/vpsadmin/www/webpage --keylength ec-256
   ```

   ::: warning 说明
   `ECC`证书的主要优势在于它的 Keysize 更小，意味着同等大小下安全性的提升和加密解密速度的加快。如 ECC-256bit 的强度大约相当于 RSA-3072bit，何乐而不为呢？当然，有人说 ECC 证书握手会明显更快，这我觉得就有些夸张了，因为 RSA 握手也没有太慢，就算有差别应该也是毫秒级，很难直接感知。

   另外，如果有些网站确实需要兼容某些古老设备的，那也还是请按需选择`RSA`证书。
   :::

2. 你最终应该看到类似这样的提示：

   ```log
   [Wed 30 Dec 2022 04:25:12 AM EST] Using ACME_DIRECTORY: https://acme-staging-v02.api.letsencrypt.org/directory
   [Wed 30 Dec 2022 04:25:13 AM EST] Using CA: https://acme-staging-v02.api.letsencrypt.org/directory
   [Wed 30 Dec 2022 04:25:13 AM EST] Create account key ok.
   [Wed 30 Dec 2022 04:25:13 AM EST] Registering account: https://acme-staging-v02.api.letsencrypt.org/directory
   [Wed 30 Dec 2022 04:25:13 AM EST] Registered
   [Wed 30 Dec 2022 04:25:13 AM EST] ACCOUNT_THUMBPRINT='CU6qmPKuRqhyTAIrF4swosR375194z_1ddUlWef8xDc'
   [Wed 30 Dec 2022 04:25:13 AM EST] Creating domain key
   [Wed 30 Dec 2022 04:25:13 AM EST] The domain key is here: /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.key
   [Wed 30 Dec 2022 04:25:13 AM EST] Single domain='二级域名.你的域名.com'
   [Wed 30 Dec 2022 04:25:13 AM EST] Getting domain auth token for each domain
   [Wed 30 Dec 2022 04:25:14 AM EST] Getting webroot for domain='二级域名.你的域名.com'
   [Wed 30 Dec 2022 04:25:14 AM EST] Verifying: 二级域名.你的域名.com
   [Wed 30 Dec 2022 04:25:23 AM EST] Pending
   [Wed 30 Dec 2022 04:25:25 AM EST] Success
   [Wed 30 Dec 2022 04:25:25 AM EST] Verify finished, start to sign.
   [Wed 30 Dec 2022 04:25:25 AM EST] Lets finalize the order.
   [Wed 30 Dec 2022 04:25:25 AM EST] Le_OrderFinalize='https://acme-staging-v02.api.letsencrypt.org/acme/finalize/490205995/7730242871'
   [Wed 30 Dec 2022 04:25:25 AM EST] Downloading cert.
   [Wed 30 Dec 2022 04:25:25 AM EST] Le_LinkCert='https://acme-staging-v02.api.letsencrypt.org/acme/cert/xujss5xt8i38waubafz2xujss5xt8i38waubz2'
   [Wed 30 Dec 2022 15:21:52 AM EST] Cert success.
   --BEGIN CERTIFICAT--
   sxlYqPvWreKgD5b8JyOQX0Yg2MLoRUoDyqVkd31PthIiwzdckoh5eD3JU7ysYBtN
   cTFK4LGOfjqi8Ks87EVJdK9IaSAu7ZC6h5to0eqpJ5PLhaM3e6yJBbHmYA8w1Smp
   wAb3tdoHZ9ttUIm9CrSzvDBt6BBT6GqYdDamMyCYBLooMyDEM4CUFsOzCRrEqqvC
   2mTTEmhvpojo5rhdTSJxibozyNWTGwoTj0v9pTUeQcGqLIzqi4DowjBHD5guwRid
   SjAFnm6JT2xUQgWFm58A1gv1OhbH1TRPUUmtE1nFEN7YiSjI4xgxqAXT3CLD2EUb
   wXlUrO6c75zSsQP4bRMzgOjJUqHtSb6IEqELzt4M7KzL5iCOruCChCo2DZxUwvVX
   tOoaAyQJzCbTqE6aUqwiKi3gVyoxvDP9mI5JdRYzsDL6GVud7EHPnYeMl9ubLZAK
   0vg84mbMP3f6mYM4KRa1cqiyOIcQPT4AzGFYVv4sm049bZQg7sd0Bz9CaFvE7yDA
   1y17XlgCDnsjxl66bqI1vkENN9XT5xeFHONqc18b5fZEKSIvdX7iWPFWp1PyMPpG
   0pMCP1EymZNFxIMJLgbWqExwLWfPc5Ib3PjBaIqhXPnw6sT2MQSxXwDupq1UJVhV
   7E3hQRVlwI4CXi6WLHJMNvNRyyK87gCrLH1bKYsPeRVaz77poWBq49zwBCts6hPY
   IeF4ltGXyANNIOPEi8vy138fRU4LYh81d8FjOtFfJZogMjwhfNvapqxPMsioPlmX
   TnZu0n7setrVNUEfTMHWqPpDgk5MPrWLA4LapqaDfEX4pwnQJLMwMi6s94z165c0
   iMRSKA1yU5zqv8aNsDfPoY4OkSPWs4MaXgRRSLBsUfZ15DwQXPk76kegHIyxWvwF
   tYw9HKR5QCMK66fa0z4aJoFVFLK0IIOGEZOanRFUCnkLUDd3QZ3YU8lEcrj7Uxos
   haiRNICyC6UfsCJ94a8vcNyMosPv3xBLMp19WXgiFYqEFQkntkv1FLRI35fjeJmg
   0fmD9VG9bkzGPHihJgQLRlCHasGf6XrdfkSsODAyCUHUHJ0RzqF4YEZMcxDxzuQ2
   YO7bFwj7S3mUdVPZ6MPasjxdyBjJgEBMch2uy4AhmudXfEBQBye8W6ZI4ztZjLVV
   FmP4SIuaNUmMe20TjR8b9NVC96AhxOanWT3mRROsdokpKQGTJvl27EHH8KuAbUOc
   G6KtPy4wslNZNXWcBy9n63RcWak12r7kAIFn38tZxmlw2WUKoRSMAH64GcDTjRQd
   Am65hBHzvGrj93wEuVNIebvNIsJOlng3HFjpIxVqKGMCIfWIKGDE3YzK3p4LbGZ6
   NZFQWYJLNVf2M9CCJfbEImPYgvctrxl39H6KVYPCw1SAdaj9NneUqmREOQkKoEB0
   x6PmNirbMscHhQPSC0JQaqUgaQFgba1ALmzRYAnYhNb0twkTxWbY7DBkAarxqMIp
   yiLKcBFc5H7dgJCImo7us7aJeftC44uWkPIjw9AKH=
   --END CERTIFICAT--
   [Wed 30 Dec 2022 15:21:52 AM EST] Your cert is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.cer
   [Wed 30 Dec 2022 15:21:52 AM EST] Your cert key is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.key
   [Wed 30 Dec 2022 15:21:52 AM EST] The intermediate CA cert is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/ca.cer
   [Wed 30 Dec 2022 15:21:52 AM EST] And the full chain certs is there:  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/fullchain.cer
   ```

3. 注意：这里申请的是测试证书，没办法直接用的，只是用来证明你的域名、配置全都正确。仔细观察，你会发现给你发证书的域名是 `https://acme-staging-v02.api.letsencrypt.org`，这个 `staging` 你就理解成【测试服】吧！

4. 如果这一步出错的话，你可以运行下面的命令，来查看详细的申请过程和具体的错误。（看不懂就隐藏掉敏感信息后，去 Xray 群里问吧）

   ```shell
   acme.sh --issue --server letsencrypt_test -d 二级域名.你的域名.com -w /home/vpsadmin/www/webpage --keylength ec-256 --debug
   ```

   嗯没错，就是在命令的最后加了一个 `--debug` 参数

5. 这一步确定成功之后，就可以申请正式的证书了。（测试证书不需要删除，它会自动被正式证书覆盖）

## 6.4 正式证书申请

1. 申请正式证书的命令如下（即将 `--server letsencrypt_test` 参数修改为 `--server letsencrypt`，并在最后加入 `--force`参数）：

   ```shell
   acme.sh --set-default-ca --server letsencrypt
   ```

   ```shell
   acme.sh --issue -d 二级域名.你的域名.com -w /home/vpsadmin/www/webpage --keylength ec-256 --force
   ```

   ::: warning 说明
   `--force` 参数的意思就是，在现有证书到期前，手动（强行）更新证书。上一步我们从“测试服”申请的证书虽然不能直接用，但是它本身是尚未过期的，所以需要用到这个参数。
   :::

2. 你最终应该看到跟上面很像的提示：

   ```log
   vpsadmin@vps-server:~$ acme.sh --issue -d 二级域名.你的域名.com -w /home/vpsadmin/www/webpage --keylength ec-256
   [Wed 30 Dec 2022 15:22:51 AM EST] Using CA: https://acme-v02.api.letsencrypt.org/directory
   [Wed 30 Dec 2022 15:22:51 AM EST] Creating domain key
   [Wed 30 Dec 2022 15:22:51 AM EST] The domain key is here: /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.key
   [Wed 30 Dec 2022 15:22:51 AM EST] Single domain='二级域名.你的域名.com'
   [Wed 30 Dec 2022 15:22:51 AM EST] Getting domain auth token for each domain
   [Wed 30 Dec 2022 15:22:51 AM EST] Getting webroot for domain='二级域名.你的域名.com'
   [Wed 30 Dec 2022 15:22:51 AM EST] Verifying: 二级域名.你的域名.com
   [Wed 30 Dec 2022 15:22:51 AM EST] Pending
   [Wed 30 Dec 2022 15:22:51 AM EST] Success
   [Wed 30 Dec 2022 15:22:51 AM EST] Verify finished, start to sign.
   [Wed 30 Dec 2022 15:22:51 AM EST] Lets finalize the order.
   [Wed 30 Dec 2022 15:22:51 AM EST] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/490205996/7730242872'
   [Wed 30 Dec 2022 15:22:51 AM EST] Downloading cert.
   [Wed 30 Dec 2022 15:22:51 AM EST] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/vsxvk0oldnuobe51ayxz4dms62sk2dwmw9zhuw'
   [Wed 30 Dec 2022 15:22:51 AM EST] Cert success.
   --BEGIN CERTIFICAT--
   sxlYqPvWreKgD5b8JyOQX0Yg2MLoRUoDyqVkd31PthIiwzdckoh5eD3JU7ysYBtN
   cTFK4LGOfjqi8Ks87EVJdK9IaSAu7ZC6h5to0eqpJ5PLhaM3e6yJBbHmYA8w1Smp
   wAb3tdoHZ9ttUIm9CrSzvDBt6BBT6GqYdDamMyCYBLooMyDEM4CUFsOzCRrEqqvC
   2mTTEmhvpojo5rhdTSJxibozyNWTGwoTj0v9pTUeQcGqLIzqi4DowjBHD5guwRid
   SjAFnm6JT2xUQgWFm58A1gv1OhbH1TRPUUmtE1nFEN7YiSjI4xgxqAXT3CLD2EUb
   wXlUrO6c75zSsQP4bRMzgOjJUqHtSb6IEqELzt4M7KzL5iCOruCChCo2DZxUwvVX
   tOoaAyQJzCbTqE6aUqwiKi3gVyoxvDP9mI5JdRYzsDL6GVud7EHPnYeMl9ubLZAK
   0vg84mbMP3f6mYM4KRa1cqiyOIcQPT4AzGFYVv4sm049bZQg7sd0Bz9CaFvE7yDA
   1y17XlgCDnsjxl66bqI1vkENN9XT5xeFHONqc18b5fZEKSIvdX7iWPFWp1PyMPpG
   0pMCP1EymZNFxIMJLgbWqExwLWfPc5Ib3PjBaIqhXPnw6sT2MQSxXwDupq1UJVhV
   7E3hQRVlwI4CXi6WLHJMNvNRyyK87gCrLH1bKYsPeRVaz77poWBq49zwBCts6hPY
   IeF4ltGXyANNIOPEi8vy138fRU4LYh81d8FjOtFfJZogMjwhfNvapqxPMsioPlmX
   TnZu0n7setrVNUEfTMHWqPpDgk5MPrWLA4LapqaDfEX4pwnQJLMwMi6s94z165c0
   iMRSKA1yU5zqv8aNsDfPoY4OkSPWs4MaXgRRSLBsUfZ15DwQXPk76kegHIyxWvwF
   tYw9HKR5QCMK66fa0z4aJoFVFLK0IIOGEZOanRFUCnkLUDd3QZ3YU8lEcrj7Uxos
   haiRNICyC6UfsCJ94a8vcNyMosPv3xBLMp19WXgiFYqEFQkntkv1FLRI35fjeJmg
   0fmD9VG9bkzGPHihJgQLRlCHasGf6XrdfkSsODAyCUHUHJ0RzqF4YEZMcxDxzuQ2
   YO7bFwj7S3mUdVPZ6MPasjxdyBjJgEBMch2uy4AhmudXfEBQBye8W6ZI4ztZjLVV
   FmP4SIuaNUmMe20TjR8b9NVC96AhxOanWT3mRROsdokpKQGTJvl27EHH8KuAbUOc
   G6KtPy4wslNZNXWcBy9n63RcWak12r7kAIFn38tZxmlw2WUKoRSMAH64GcDTjRQd
   Am65hBHzvGrj93wEuVNIebvNIsJOlng3HFjpIxVqKGMCIfWIKGDE3YzK3p4LbGZ6
   NZFQWYJLNVf2M9CCJfbEImPYgvctrxl39H6KVYPCw1SAdaj9NneUqmREOQkKoEB0
   x6PmNirbMscHhQPSC0JQaqUgaQFgba1ALmzRYAnYhNb0twkTxWbY7DBkAarxqMIp
   yiLKcBFc5H7dgJCImo7us7aJeftC44uWkPM=
   --END CERTIFICAT--
   [Wed 30 Dec 2022 15:22:52 AM EST] Your cert is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.cer
   [Wed 30 Dec 2022 15:22:52 AM EST] Your cert key is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/二级域名.你的域名.com.key
   [Wed 30 Dec 2022 15:22:52 AM EST] The intermediate CA cert is in  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/ca.cer
   [Wed 30 Dec 2022 15:22:52 AM EST] And the full chain certs is there:  /home/vpsadmin/.acme.sh/二级域名.你的域名.com_ecc/fullchain.cer
   ```

3. 仔细观察，你会发现这次给你发证书的域名是 `https://acme-v02.api.letsencrypt.org`，少了 `staging`，自然就是【正式服】了！

## 6.5 证书安装

1. 证书申请完成后，需要安装，安装到指定位置，并在配置文件中引用即可：

   ```shell
   vpsadmin@vps-server:~$ acme.sh --installcert -d 二级域名.你的域名.com --cert-file /你要安装到的位置/cert.crt --key-file /你要安装到的位置/cert.key --fullchain-file /你要安装到的位置/fullchain.crt --ecc
   [Mon 14 Feb 2022 03:00:25 PM CST] Installing cert to: /etc/xray/cert/cert.crt
   [Mon 14 Feb 2022 03:00:25 PM CST] Installing key to: /etc/xray/cert/cert.key
   [Mon 14 Feb 2022 03:00:25 PM CST] Installing full chain to: /etc/xray/cert/fullchain.crt
   ```

## 6.6 你的进度

至此，Xray 所需要的两个基础设施终于全部就位！千呼万唤始出来的 Xray 马上就要揭开面纱，我们终于要进入最激动人心章节啦！

> ⬛⬛⬛⬛⬛⬛⬜⬜ 75%

---

---
url: /document/level-0/ch07-xray-server.md
---
# 【第 7 章】Xray 服务器篇

## 7.1 博观而约取，厚积而薄发

本文撰写过程中，大佬开玩笑的吐槽到：你这教程，居然连载了 6 章都还没到 Xray，不知道的还以为你是“手把手教你建网站”教程呢。（我竟无法反驳.jpg!）

其实这样的结构是我多番思考之后的决定，毕竟只有打好基础，才能在后面事半功倍快速反超。我在群里看到许多新人连`nano`都无法正确使用，也不会用`WinSCP`，远程手写编辑出来的`config.json`自然错误百出，连查错也变得举步维艰。

::: warning
经过了前 6 章的准备，各位已经跟我一起翻越了 Linux 基本操作、VPS 远程管理、网页搭建、域名管理、证书申请等等几座大山。是不是回头看看，觉得其实非常简单呢？现在我们有了如此扎实的准备，接下来安装和配置 Xray
时会有一种【水到渠成】的轻快感觉。
:::

后面要做的事情非常简单：

1. 安装
2. 配置（如安装 TLS 证书、`config.json`）
3. 运行
4. 优化（如更新内核、开启`bbr`、网站`http`访问自动跳转`https`等）

## 7.2 安装 Xray

首先，Xray 的官方载体，就是 [xray-core](https://github.com/XTLS/Xray-core) 开源项目（基于 `MPL 2.0`
开源协议）生成的二进制程序。你把这个二进制放在服务器运行，它就是服务器端；你把它下载到本地电脑运行，它就是客户端。主要区别来源于【配置】。

安装时，直接使用官方安装脚本就很简单直接。它提供了多种安装选项，有兴趣的可以去官方的[安装脚本仓库](https://github.com/XTLS/Xray-install)中看看脚本的说明，**本文使用的是【非 root
用户】安装模式**。

写本文时，安装脚本在使用非 root 账户时有一些小 bug，所以我决定正好把这几步分开操作，可以顺便说明一下 Linux 下的删除命令。

1. 小小白白 Linux 基础命令：

   |   编号   | 命令名称 | 命令说明 |
   | :------: | :------: | :------: |
   | `cmd-14` |   `rm`   | 删除命令 |

2. 将安装脚本下载至本地：

   ```shell
   wget https://github.com/XTLS/Xray-install/raw/main/install-release.sh
   ```

3. 执行安装命令

   ```shell
   sudo bash install-release.sh
   ```

4. 使用完成之后可以删除该脚本

   ```shell
   rm ~/install-release.sh
   ```

   ::: warning
   使用 `rm` 命令删除文件的时候，默认其实就是删除现在所在的文件夹下的文件。但是，**我依然写了完整的路径**： `~/install-release.sh`，这是我使用 `rm` 时的一个安全习惯、也是我把安装分成几步之后想强调一下的内容。如果你听过一些“程序员从删库到跑路”之类的段子，大概就知道为什么了。
   :::

5. 完整流程演示如下：

   ![Xray服务器端安装流程演示](./ch07-img01-xray-install.gif)

## 7.3 给 Xray 配置 TLS 证书

虽然我们前面已经申请好了 TLS
证书，但是按照 [`acme.sh`的官方说明](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E#3-copy%E5%AE%89%E8%A3%85-%E8%AF%81%E4%B9%A6)，申请后的证书不建议直接使用。正确的方法是使用 `--install-cert`
命令安装给需要的程序。我们现在就来把证书安装给 `xray-core` 使用。

1. 为了规避非 root 账户的各种潜在的权限困扰，我们在 vpsadmin 账户下建立一个证书文件夹

   ```shell
   mkdir ~/xray_cert
   ```

2. 使用`acme.sh`的`--install-cert`正确安装（拷贝）证书文件

   ```shell
   acme.sh --install-cert -d 二级域名.你的域名.com --ecc \
               --fullchain-file ~/xray_cert/xray.crt \
               --key-file ~/xray_cert/xray.key
   ```

3. `xray.key`文件默认对其他用户不可读，所以需要赋予其可读性权限

   ```shell
   chmod +r ~/xray_cert/xray.key
   ```

4. 过程比较简单就不放动图了：

   ![Xray服务器端安装流程演示](./ch07-img02-xray-cert-install.png)

5. `acme.sh` 会每天检查一次并自动更新有效期短于 30 天的证书，Xray 默认会自动热重载证书，无需担心后续证书更新的问题。

## 7.4 配置 Xray

首先，各种配置都可以参考[官方 VLESS 配置示例](https://github.com/XTLS/Xray-examples)。本文会基于官方示例，配置一个最精简的方式：【单 `VLESS` 协议入站 + `80`
端口回落】，满足大多数场景的最大速度及必要安全。

1. 生成一个合法的 `UUID` 并保存备用（`UUID`可以简单粗暴的理解为像指纹一样几乎不会重复的 ID）

   ```shell
   xray uuid
   ```

2. 建立日志文件及文件夹备用
   1. 小小白白 Linux 基础命令：
      | 编号 | 命令名称 | 命令说明 |
      |:--:|:--:|:--:|
      | `cmd-15` | `touch` | 建立空白文件 |

   2. 在`vpsadmin`的文件夹内建立一个【日志专用文件夹】

      ```shell
      mkdir ~/xray_log
      ```

   3. 生成所需的两个日志文件（访问日志、错误日志）

      ```shell
      touch ~/xray_log/access.log && touch ~/xray_log/error.log
      ```

      ::: warning
      这个位置不是`Xray`标准的日志文件位置，放在这里是避免权限问题对新人的操作带来困扰。当你熟悉之后，建议回归默认位置： `/var/log/xray/access.log`
      和 `/var/log/xray/error.log` 。
      :::

   4. 因为 Xray 默认是 nobody 用户使用，所以我们需要让其他用户也有“写”的权限（`*.log` 就是所有文件后缀是`log`的文件，此时`CLI`界面的效率优势就逐渐出现了）
      ```shell
      chmod a+w ~/xray_log/*.log
      ```

3. 使用`nano`创建`Xray`的配置文件

   ```shell
   sudo nano /usr/local/etc/xray/config.json
   ```

4. 将下面的文件全部复制进去，并将之前生成的`UUID`填入第 61 行 `"id": "",` 之中。（填好之后的样子是 `"id": "uuiduuid-uuid-uuid-uuid-uuiduuiduuid"`
   ），本文的这个配置文件中增加了我的各种啰嗦注解，以方便你理解每一个配置模块的功能是什么。

   ```json
   // REFERENCE:
   // https://github.com/XTLS/Xray-examples
   // https://xtls.github.io/config/
   // 常用的 config 文件，不论服务器端还是客户端，都有 5 个部分。外加小小白解读：
   // ┌─ 1*log 日志设置 - 日志写什么，写哪里（出错时有据可查）
   // ├─ 2_dns DNS-设置 - DNS 怎么查（防 DNS 污染、防偷窥、避免国内外站匹配到国外服务器等）
   // ├─ 3_routing 分流设置 - 流量怎么分类处理（是否过滤广告、是否国内外分流）
   // ├─ 4_inbounds 入站设置 - 什么流量可以流入 Xray
   // └─ 5_outbounds 出站设置 - 流出 Xray 的流量往哪里去
   {
     // 1\_日志设置
     "log": {
       "loglevel": "warning", // 内容从少到多: "none", "error", "warning", "info", "debug"
       "access": "/home/vpsadmin/xray_log/access.log", // 访问记录
       "error": "/home/vpsadmin/xray_log/error.log" // 错误记录
     },
     // 2_DNS 设置
     "dns": {
       "servers": [
         "https+local://1.1.1.1/dns-query", // 首选 1.1.1.1 的 DoH 查询，牺牲速度但可防止 ISP 偷窥
         "localhost"
       ]
     },
     // 3*分流设置
     "routing": {
       "domainStrategy": "IPIfNonMatch",
       "rules": [
         // 3.1 防止服务器本地流转问题：如内网被攻击或滥用、错误的本地回环等
         {
           "ip": [
             "geoip:private" // 分流条件：geoip 文件内，名为"private"的规则（本地）
           ],
           "outboundTag": "block" // 分流策略：交给出站"block"处理（黑洞屏蔽）
         },
         {
           // 3.2 防止服务器直连国内
           "ip": ["geoip:cn"],
           "outboundTag": "block"
         },
         // 3.3 屏蔽广告
         {
           "domain": [
             "geosite:category-ads-all" // 分流条件：geosite 文件内，名为"category-ads-all"的规则（各种广告域名）
           ],
           "outboundTag": "block" // 分流策略：交给出站"block"处理（黑洞屏蔽）
         }
       ]
     },
     // 4*入站设置
     // 4.1 这里只写了一个最简单的 vless+xtls 的入站，因为这是 Xray 最强大的模式。如有其他需要，请根据模版自行添加。
     "inbounds": [
       {
         "port": 443,
         "protocol": "vless",
         "settings": {
           "users": [
             {
               "id": "", // 填写你的 UUID
               "flow": "xtls-rprx-vision",
               "level": 0,
               "email": "vpsadmin@yourdomain.com"
             }
           ],
           "decryption": "none",
           "fallbacks": [
             {
               "dest": 80 // 默认回落到防探测的代理
             }
           ]
         },
         "streamSettings": {
           "network": "tcp",
           "security": "tls",
           "tlsSettings": {
             "alpn": "http/1.1",
             "certificates": [
               {
                 "certificateFile": "/home/vpsadmin/xray_cert/xray.crt",
                 "keyFile": "/home/vpsadmin/xray_cert/xray.key"
               }
             ]
           }
         }
       }
     ],
     // 5*出站设置
     "outbounds": [
       // 5.1 第一个出站是默认规则，freedom 就是对外直连（vps 已经是外网，所以直连）
       {
         "tag": "direct",
         "protocol": "freedom"
       },
       // 5.2 屏蔽规则，blackhole 协议就是把流量导入到黑洞里（屏蔽）
       {
         "tag": "block",
         "protocol": "blackhole"
       }
     ]
   }
   ```

5) 完整流程演示如下：
   ![创建日志文件及config.json配置文件](./ch07-img04-xray-log-and-config.gif)

## 7.5 启动 Xray 服务！！（并查看服务状态）

如果你是跟随本文一步步设置过来，其实就已经避开了最常见**日志文件权限不足**、**证书文件权限不足** 这两个大坑。那么现在运行`Xray`自然应该无比顺利。

1. 输入下面的命令，享受启动`Xray`的历史性时刻吧！！！

```shell
sudo systemctl start xray
```

2. 仅仅`start`我们并不能确定是否成功的开启了 Xray 的服务，要确定它的状态，就要用到下面的命令。

```shell
sudo systemctl status xray

```

看到那个绿色的、令人愉悦的 `active (running)` 了吗？它就是说 `Xray` 已经在正确的运行了

3. 完整流程演示如下：

![启动并查看Xray运行状态](./ch07-img05-xray-start-and-status.gif)

## 7.6 回顾 `systemd` 进行基本的服务管理

到现在为止，我们已经使用过了`systemctl`相关的`start`, `status`, `reload` 等命令，这些都是基于`systemd`管理模块对 Linux
系统中各种服务进行管理的通用命令。现在正好熟悉一下相关的其他几个命令。

1. 若你需要暂时关闭 `Xray` 的服务，那就用`stop`命令

```shell
sudo systemctl stop xray
```

2. 若你需要重启`Xray`的服务，那就用`restart`命令

```shell
sudo systemctl restart xray
```

3. 若你需要禁用`Xray`的服务（电脑重启后禁止 Xray 自动运行），那就用`disable`命令

```shell
sudo systemctl disable xray
```

4. 若你需要启用`Xray`的服务（电脑重启后确保 Xray 自动运行），那就用`enable`命令

```shell
sudo systemctl enable xray
```

## 7.7 服务器优化之一：开启 BBR

1. 传说中的`BBR`

我相信，你在搜索各种科学上网技术的时候，肯定不止一次的听过`bbr`这个东西，在各种博客添油加醋之下，让人觉得它神乎其神。更有`bbrplus`, `bbr2`, `魔改bbr` 等一大堆衍生品。仿佛神油一般，用了就能野鸡线路变专线。

那么，这东西究竟是什么？它有没有用？又该用哪一个版本呢？

2. 实际的`BBR`

**BBR** = **B**ottleneck **B**andwidth and **R**ound-trip propagation time，是一种 TCP 的**拥塞控制算法**。简单粗暴的理解就是**数据流量的交通管理**
：当公路不再塞车的时候，每辆车自然就能保持较快的车速了。

那么它有没有用呢？一般而言，`有BBR` 和 `没有BBR` 会有可以感知的差别（速度、稳定性、延迟方面都会有一些改善），所以 **【非常建议开启 `BBR`】**。

但开启之后，`BBR` 在 `4.x` 和 `5.x` 之间的差异往往比较微妙、见仁见智，造成体验差别的决定性因素仍然是线路质量。所以 **【不必纠结版本、不必盲目追新、跟随你的发行版更新内核即可】**

3. `bbrplus`, `bbr2`, `魔改bbr` 和其他各种听起来就酷炫的版本是不是更好？

一句话：**不是！不要用这些！这些都为了吸引眼球乱起的名字！**

BBR 是内核自带跟随内核发布并更新的，而这些名字看起来很酷炫的东西，说白了就是作者自行调整 `BBR` 参数以及使用这些参数构建出的内核，这些脚本也仅仅就是下载安装这些魔改内核。

内核的稳定是一台服务器稳定运行的基石。**【BBR 魔改版带来的细微性能差异绝对不值得更换不稳定的内核。】** 请选择你所在的 Linux 发行版所支持的最新内核，这样可以最大限度的保持服务器的长期稳定和兼容。

::: warning
所谓魔改`bbr`的【领先】是有非常强的时效性的。比如很多 `bbrplus` 脚本，因为几年来都没有更新，到现在还会把你的内核换成很老的版本，导致你的系统失去安全性甚至无法启动。
:::

4. `fq`, `fq_codel`, `fq_pie`, `cake`和其他算法哪个好？

一句话：**看不懂的话，请保持`fq`，足够、且不会劣化你的线路**

5. 锐速、Finalspeed、LotServer 和其他“加速工具”

一句话：**不要用这些！把他们丢进历史的垃圾桶吧！**

它能解决的也只有丢包率的问题。不太准确的比喻，就是本来你用一辆车送你的货，有时候车半路就坏了（丢包），用了这些以后，你直接派出 3
份一样的货，让三辆车同时送，只要有一辆没坏就能送到。马路上都是你的车，自然就能把别人挤下去。但可想而知，你挤别人的时候，别人也会来挤你，而整个机房的出口道路一共就那么宽，最终势必就变成集体大堵车了。

::: warning 说明
它们的原理不是算法优化、不是提速、大多数是简单粗暴的**多倍发包**。对于【丢包率非常高】的差线路可能有一点作用，但【对丢包率低的好线路没有任何优化作用，反而会成倍的消耗你的流量】，进而造成服务器和你的邻居不必要的压力。

如果你的线路真的丢包率奇高，真正靠谱的解决方案是【换线路】。
:::

6. 啰嗦了这么多，就是因为围绕 `BBR` 忽悠小白的错误概念和坑人脚本实在是太多了。我希望你们现在对 `BBR` 有了相对清晰的理解。接下来，我们就动手开启`BBR` 吧！

7. 添加参数配置文件并指定开启 `BBR`

```shell
sudo nano /etc/sysctl.d/99-bbr.conf
```

8. 把下面的内容添加进去

```
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
```

9. 读取配置，使 `BBR` 设置生效

```shell
sudo sysctl --system
```

10. 确认`BBR`开启

如果你想确认 `BBR` 是否正确开启，可以使用下面的命令：

```shell
sysctl net.ipv4.tcp_congestion_control
```

此时应该返回这样的结果：

```
net.ipv4.tcp_congestion_control = bbr
```

如果你在疑惑这个 `ipv4`，无需在意，只是历史命名遗留，实际上对 `ipv6` 也是生效的。

如果你想确认 `fq` 算法是否正确开启，可以使用下面的命令：

```shell
sysctl net.core.default_qdisc
```

此时应该返回这样的结果：

```
net.core.default_qdisc = fq
```

## 7.8 服务器优化之二：开启 HTTP 自动跳转 HTTPS

1. 之前我们已经搭建了 `80` 端口的 `http` 网页，并以此申请了 TLS 证书。

但如果你尝试过用浏览器访问我们的这个界面，就会发现 `http` 访问并不会像大多数网站一样自动升级为 `https` 访问。换言之，我们现在的设置下，`http(80)` 和 `https(443)`
之间完全是独立的。如果要解决这个问题，就需要做一些修改。

2. 编辑 Nginx 的配置文件

```shell
sudo nano /etc/nginx/nginx.conf
```

3. 在我们设置过的 80 端口 Server 中加入下面的语句，并保存退出（可同时删除`root`和`index`两行）

```
return 301 https://$http_host$request_uri;
```

4. 在与 `80` 端口同级的位置增加一个本地端口监听来提供网页展示。本文以 `8080` 端口做演示。（可以是任意端口）

```
server {
   listen 127.0.0.1:8080;
   root /home/vpsadmin/www/webpage;
   index index.html;
   add_header Strict-Transport-Security "max-age=63072000" always;
}
```

5. 重启 Nginx 服务

```shell
sudo systemctl restart nginx
```

6. 修改 Xray 的回落设置，将回落从 `80` 端口改为 `8080` 端口。（找到 `"dest": 80`, 并改成 `"dest": 8080`）

```shell
sudo nano /usr/local/etc/xray/config.json
```

7. 重启 `Xray` 服务，即完成了设置

```shell
sudo systemctl restart xray
```

8. 完整流程演示如下：

![http自动跳转https](./ch07-img07-http-to-https.gif)

9. 当你输入 `http://a-name.yourdomain.com`的时候，它应该已经会自动跳转 https 了

![http自动跳转https生效](./ch07-img08-http-to-https-check.png)

## 7.9 服务器优化之三：更丰富的回落

如果你需要更丰富的回落功能，可以参考 [《回落 (fallbacks) 功能简析》](../level-1/fallbacks-lv1.md)

## 7.10 你的进度

恭喜！！到这一步，你已经拥有了可以正常科学上网的服务器、同时也有了可以防止主动探测攻击的伪装网站。接下来，只要给你的客户端装上合适的软件，就可以享受顺畅的网络了！

> ⬛⬛⬛⬛⬛⬛⬛⬜ 87.5%

## 7.11 重要勘误

1. 初版中`Xray`配置文件`config.json`文件夹位置错误。若你已经根据之前的位置进行了操作，`Xray`会无法正确启动。故勘误说明于此，请自查，造成不便十分抱歉！

* 正确位置：`/usr/local/etc/xray/config.json`
* 错误位置：`/usr/local/etc/config.json`

受影响章节：

* 7.4 配置`Xray` - 3. 使用`nano`创建`Xray`的配置文件
* 7.8 服务器优化之二 - 6. 修改`Xray`的回落设置

2. 初版中修改`Nginx`配置文件`nginx.conf`时内容错误（网页文件夹位置错误），若你已经根据之前的位置进行了操作，`Nginx`会无法找到正确的网站。请自查，造成不便十分抱歉！

* 正确文件夹位置：`root /home/vpsadmin/www/webpage;`
* 错误文件夹位置：`root /var/www/website/html`

受影响章节：

* 7.8 服务器优化之二 - 4. 在与 `80` 端口同级的位置增加一个本地端口监听来提供网页展示

---

---
url: /document/level-0/ch08-xray-clients.md
---
# 【第 8 章】Xray 客户端篇

## 8.1 Xray 的工作原理简述

要正确的配置和使用`Xray`，就需要正确的理解其工作原理，对于新人，可以先看看下面简化的示意图（省略了许多复杂的设置）：

![Xray数据流向](./ch08-img01-flow.png)

这其中的关键点是：

1. APP 要主动或借助转发工具，将数据【流入(`inbounds`)】`Xray` 客户端

2. 流量进入客户端后，会被【客户端路由(`routing`)】按规则处理后，向不同方向【流出`(outbounds)`】`Xray` 客户端。比如：
   1. 国内流量直连（`direct`）
   2. 国外流量转发 VPS（`proxy`）
   3. 广告流量屏蔽（`block`）

3. 向 VPS 转发的国外流量，会跨过防火墙，【流入(`inbounds`)】 `Xray` 服务器端

4. 流量进入服务器端后，与客户端一样，会被【服务器端路由(`routing`)】按规则处理后，向不同方向【流出`(outbounds)`】：
   1. 因为已经在防火墙之外，所以流量默认直连，你就可以访问到不存在网站们了（`direct`）
   2. 如果需要在不同的 VPS 之间做链式转发，就可以继续配置转发规则（`proxy`）
   3. 你可以在服务器端继续禁用各种你想禁用的流量，如广告、BT 下载等（`block`）

:::warning 注意

请务必记得，`Xray` 的路由配置非常灵活，上面的说明只是无限可能性中的一种。

借助 `geosite.dat` 和 `geoip.dat` 这两个文件，可以很灵活的从【域名】和【IP】这两个角度、不留死角的控制流量流出的方向。这比曾经单一笼统的 `GFWList` 强大很多很多，可以做到非常细致的微调：比如可以指定 Apple 域名直连或转发、指定亚马逊域名代理或转发，百度的域名屏蔽等等。。。）

现在，[《路由 (routing) 功能简析》](../level-1/routing-lv1-part1.md) 已经上线，我建议对路由功能有兴趣的同学，先继续跟着本文完成客户端的基础配置，之后再去这里详细学习。
:::

## 8.2 客户端与服务器端正确连接

现在你已经理解了 `Xray` 的工作原理，那么接下来的配置，其实就是【告诉你的客户端如何连接 VPS 服务器】。这和你已经很熟悉的、告诉`PuTTY`如何远程连接服务器是一样的。只不过 Xray 连接时的要素不止是【IP 地址】+【端口】+【用户名】+【密码】这四要素了。

实际上，`Xray`的连接要素是由不同的[协议](../../config/inbounds/)决定的。本文在第 7 章的配置文件 `config.json` 里，我们使用 `Xray` 下独特而强大的 `VLESS` 协议 + `XTLS` 流控。所以看看那个配置文件的内容就能知道，这个协议组合的连接要素有：

* 服务器【地址】: `a-name.yourdomain.com`
* 服务器【端口】: `443`
* 连接的【协议】: `vless`
* 连接的【流控】: `xtls-rprx-vision` (vision 模式适合全平台)
* 连接的【验证】: `uuiduuid-uuid-uuid-uuiduuiduuid`
* 连接的【安全】: `"allowInsecure": false`

鉴于新人一般都会使用手机 APP 或者电脑的 GUI 客户端，我就把常用的客户端罗列在下面。每个客户端都有自己独特的配置界面，逐一截图展示并不现实，所以请你务必仔细阅读这些客户端的说明、然后把上述要素填入合适的地方即可。

:::warning 注意
许多工具其实是同时支持 `xray-core` 和 `v2fly-core` 的，但默认内置的不一定是哪个，所以别忘记检查一下是否是你想要的那个在工作哦！
:::

* **v2rayN - 适用于 Windows 平台**
  * 请从它的[GitHub 仓库 Release 页面](https://github.com/2dust/v2rayN/releases)获取最新版
  * 请根据该客户端的说明进行设置

* **v2rayNG - 适用于 Android 平台**
  * 请从它的[GitHub 仓库 Release 页面](https://github.com/2dust/v2rayNG/releases)获取最新版
  * 请根据该客户端的说明进行设置

* **Shadowrocket - 适用于 iOS, 基于苹果 M 芯片的 macOS**
  * 你需要注册一个【非中国区】的 iCloud 账户
  * 你需要通过 App Store 搜索并购买
  * 请根据该客户端的说明进行设置

* **Qv2ray - 跨平台图形界面，适用于 Linux, Windows, macOS**
  * 请从它的[GitHub 仓库 Release 页面](https://github.com/Qv2ray/Qv2ray/releases)获取最新版（还可以从它的[GitHub 自动构建仓库](https://github.com/Qv2ray/Qv2ray/actions)寻找更新的版本）
  * 请从它的[项目主页](https://qv2ray.net/)学习文档
  * 请根据该客户端的说明进行设置

* **V2RayXS - 基于 V2RayX 开发的一款使用 xray-core 的 macOS 客户端**
  * 请从它的 [GitHub 仓库 Release 页面](https://github.com/tzmax/v2rayXS/releases) 获取最新版
  * 支持一键导入 [VMessAEAD / VLESS 分享链接标准提案](https://github.com/XTLS/Xray-core/issues/91) 为标准的分享链接
  * 请根据该客户端的说明进行设置

到这一步，你的全套配置就已经可以正常使用啦！

## 8.3 附加题 1：在 PC 端手工配置 `xray-core`

虽然到上面一步已经可以结束了，但是如果你是个好奇心强、记忆力好的的同学，一定会想起来我在上一章说过，你把`xray-core` 的二进制文件“放在服务器运行，它就是服务器端；你把它下载到本地电脑运行，它就是客户端。” 那究竟要怎样直接使用 `xray-core` 做客户端呢？

为了回答这个问题，我加入了附加题章节，有一点点超纲，有一点点麻烦，但费这个笔墨是因为这个方式有它的优势：

* 第一时间获得最新版而无需等待 APP 升级适配

* 灵活自由的路由配置能力（当然 GUI 客户端中 Qv2ray 的高级路由编辑器非常强大，也可以完整实现 xray-core 的路由配置功能）

* 节约系统资源 （GUI 界面一定会有资源消耗，消耗的多少则取决于客户端的实现）

它的劣势应该就是【需要手写配置文件】有点麻烦了。但其实，你想想，服务器上你已经成功的写过一次了，现在又有什么区别呢？接下来，还是老样子，我们分解一下步骤：

1. 首先请从 Xray 官方的 [GitHub 仓库 Release 页面](https://github.com/XTLS/Xray-core/releases) 获取对应平台的版本，并解压缩到合适的文件夹
2. 在合适的文件夹建立空白配置文件：`config.json` （自己常用平台下新建文件大家肯定都会，这就真不用啰嗦了）
3. 至于什么是“合适的文件夹”？这就取决于具体的平台了~
4. 填写客户端配置

   * 我就以 `8.1` 原理说明里展示的基本三类分流（国内流量直连、国际流量转发 VPS、广告流量屏蔽），结合 `8.2` 的连接要素，写成一个配置文件
   * 请将 `uuid` 替换成与你服务器一致的 `uuid`
   * 请将 `address` 替换成你的真实域名
   * 请将 `serverName` 替换成你的真实域名
   * 各个配置模块的说明我都已经（很啰嗦的）放在对应的配置点上了

   ```json
   // REFERENCE:
   // https://github.com/XTLS/Xray-examples
   // https://xtls.github.io/config/

   // 常用的config文件，不论服务器端还是客户端，都有5个部分。外加小小白解读：
   // ┌─ 1_log          日志设置 - 日志写什么，写哪里（出错时有据可查）
   // ├─ 2_dns          DNS-设置 - DNS怎么查（防DNS污染、防偷窥、避免国内外站匹配到国外服务器等）
   // ├─ 3_routing      分流设置 - 流量怎么分类处理（是否过滤广告、是否国内外分流）
   // ├─ 4_inbounds     入站设置 - 什么流量可以流入Xray
   // └─ 5_outbounds    出站设置 - 流出Xray的流量往哪里去

   {
     // 1_日志设置
     // 注意，本例中我默认注释掉了日志文件，因为windows, macOS, Linux 需要写不同的路径，请自行配置
     "log": {
       // "access": "/home/local/xray_log/access.log",    // 访问记录
       // "error": "/home/local/xray_log/error.log",    // 错误记录
       "loglevel": "warning" // 内容从少到多: "none", "error", "warning", "info", "debug"
     },

     // 2_DNS设置
     "dns": {
       "servers": [
         // 2.1 国外域名使用国外DNS查询
         {
           "address": "1.1.1.1",
           "domains": ["geosite:geolocation-!cn"]
         },
         // 2.2 国内域名使用国内DNS查询，并期待返回国内的IP，若不是国内IP则舍弃，用下一个查询
         {
           "address": "223.5.5.5",
           "domains": ["geosite:cn"],
           "expectIPs": ["geoip:cn"]
         },
         // 2.3 作为2.2的备份，对国内网站进行二次查询
         {
           "address": "114.114.114.114",
           "domains": ["geosite:cn"]
         },
         // 2.4 最后的备份，上面全部失败时，用本机DNS查询
         "localhost"
       ]
     },

     // 3_分流设置
     // 所谓分流，就是将符合否个条件的流量，用指定`tag`的出站协议去处理（对应配置的5.x内容）
     "routing": {
       "domainStrategy": "IPIfNonMatch",
       "rules": [
         // 3.1 广告域名屏蔽
         {
           "domain": ["geosite:category-ads-all"],
           "outboundTag": "block"
         },
         // 3.2 国内域名直连
         {
           "domain": ["geosite:cn"],
           "outboundTag": "direct"
         },
         // 3.3 国外域名代理
         {
           "domain": ["geosite:geolocation-!cn"],
           "outboundTag": "proxy"
         },
         // 3.4 走国内"223.5.5.5"的DNS查询流量分流走direct出站
         {
           "ip": ["223.5.5.5"],
           "outboundTag": "direct"
         },
         // 3.5 国内IP直连
         {
           "ip": ["geoip:cn", "geoip:private"],
           "outboundTag": "direct"
         }
         // 3.6 默认规则
         // 在Xray中，任何不符合上述路由规则的流量，都会默认使用【第一个outbound（5.1）】的设置，所以一定要把转发VPS的outbound放第一个
       ]
     },

     // 4_入站设置
     "inbounds": [
       // 4.1 一般都默认使用socks5协议作本地转发
       {
         "tag": "socks-in",
         "protocol": "socks",
         "listen": "127.0.0.1", // 这个是通过socks5协议做本地转发的地址
         "port": 10800, // 这个是通过socks5协议做本地转发的端口
         "settings": {
           "udp": true
         }
       },
       // 4.2 有少数APP不兼容socks协议，需要用http协议做转发，则可以用下面的端口
       {
         "tag": "http-in",
         "protocol": "http",
         "listen": "127.0.0.1", // 这个是通过http协议做本地转发的地址
         "port": 10801 // 这个是通过http协议做本地转发的端口
       }
     ],

     // 5_出站设置
     "outbounds": [
       // 5.1 默认转发VPS
       // 一定放在第一个，在routing 3.6 里面已经说明了，这等于是默认规则，所有不符合任何规则的流量都走这个
       {
         "tag": "proxy",
         "protocol": "vless",
         "settings": {
           "address": "a-name.yourdomain.com", // 替换成你的真实域名
           "port": 443,
           "id": "uuiduuid-uuid-uuid-uuid-uuiduuiduuid", // 和服务器端的一致
           "flow": "xtls-rprx-vision",
           "encryption": "none",
           "level": 0
         },
         "streamSettings": {
           "network": "tcp",
           "security": "tls",
           "tlsSettings": {
             "serverName": "a-name.yourdomain.com", // 替换成你的真实域名
             "allowInsecure": false, // 禁止不安全证书
             "fingerprint": "chrome" // 通过 uTLS 库 模拟 Chrome / Firefox / Safari 或随机生成的指纹
           }
         }
       },
       // 5.2 用`freedom`协议直连出站，即当routing中指定'direct'流出时，调用这个协议做处理
       {
         "tag": "direct",
         "protocol": "freedom"
       },
       // 5.3 用`blackhole`协议屏蔽流量，即当routing中指定'block'时，调用这个协议做处理
       {
         "tag": "block",
         "protocol": "blackhole"
       }
     ]
   }
   ```

## 8.4 附加题 2：在 PC 端手工运行 `xray-core`

写好了配置文件该，要怎么让 `xray-core` 运行起来呢？双击好像并没有反应啊？

首先，你要找到电脑上的【命令行界面】。

1. Linux 桌面、macOS 系统的同学肯定已经比较熟悉了，搜索 `Console` 或者 `Terminal` 就可以
2. Windows 就可以搜索使用 `Cmd` 或者 `Powershell` 等程序（WSL 的同学你坐下，你的 `Console` 当然也可以）

其次，我们要做的事情是【让 `xray` 找到并读取配置文件 `config.json`，然后运行】，所以：

1. 在 Windows 下，假设你的 `Xray` 程序位置是 `C:\Xray-windows-64\xray.exe`，配置文件位置是`C:\Xray-windows-64\config.json`，那么正确的启动命令就是：

   ```shell
   C:\Xray-windows-64\xray.exe -c C:\Xray-windows-64\config.json
   ```

   :::tip 说明
   这里的 `-c` 就是指定配置文件路径的参数，告诉 `xray` 去后面的位置找配置文件
   :::

2. 相似的，在 Linux 和 macOS 下，假设你的 `Xray` 程序位置是 `/usr/local/bin/xray`，配置文件位置是`/usr/local/etc/xray/config.json`，那么正确的启动命令就是

   ```shell
   /usr/local/bin/xray -c /usr/local/etc/xray/config.json
   ```

   :::tip 说明
   每个系统都有系统路径变量，所以写 `Xray` 程序时不一定要写绝对路径。但是写了肯定没错，所以我就如此演示了。
   :::

## 8.5 附加题 3：在 PC 端开机自动运行 `xray-core`

如果你真的尝试了手动运行 `xray-core`，你一定会发现这个方式还有点小问题：

1. 每次运行 `Xray` 都要出现一个黑乎乎的窗口，很丑
2. 不能开机自动运行，每次都要手工输入，十分不方便

我可以肯定的告诉你：**完全可以解决**。但是具体的解决方式，就当作课外作业留给大家吧！（友情提示，文档站的问答区有线索哦）

## 8.6 圆满完成！

我相信，有耐心看到这里的同学，都是兼具好奇心和行动力的学习派！我现在要郑重的恭喜你，因为到了这里，你已经完完整整的【**从第一条命令开始，完成了 VPS 服务器部署，并成功的在客户端配置使用 Xray**】了！这毫无疑问是一个巨大的胜利！

我相信，你现在一定对`Linux`不再恐惧，对`Xray`不再陌生了吧！

**至此，小小白白话文圆满结束！**

> ⬛⬛⬛⬛⬛⬛⬛⬛ 100%

## 8.7 TO INFINITY AND BEYOND!

**但现在你看到的，远远不是 Xray 的全貌。**

`Xray`是一个强大而丰富的网络工具集合，平台化的提供了众多模块，可以像瑞士军刀一样，通过灵活的配置组合解决各种不同的问题。而本文，仅仅蜻蜓点水的用了**最简单**、**最直观**的配置来做**基础演示**。

如果你觉得现在已经完全够用了，那就好好的享受它给你带来的信息自由。但如果你的好奇心依然不能停歇，那就去继续挖掘它无限的可能性吧！

需要更多信息，可以到这里寻找：

1. [xtls.github.io](https://xtls.github.io/) - 官方文档站
2. [官方 Telegram 群组](https://t.me/projectXray) - 活跃而友善的官方讨论社区

![TO INFINITY AND BEYOND!](./ch08-img02-buzz.png)

:::tip 不算后记的后记

希望我陪你走过的这一段小小的旅程，可以成为你网络生活中的一份小小助力。

这篇文章里的工具和信息难免会一点点的陈旧过时，但你一定会逐渐成长为大佬。未来的某个时间，若你能偶尔想起这篇教程、想起我写下本文的初衷，那我衷心希望你能够薪火相传、把最新的知识分享给后来人，让这一份小小的助力在社区里坚定的传递下去。

这是个大雪封山乌云密布的世界，人们孤独的走在各自的路上试图寻找阳光，如果大家偶尔交汇时不能守望相助互相鼓励，那最终剩下的，恐怕只有【千山鸟飞绝 万径人踪灭】的凄凉了吧。
:::

---

---
url: /document/level-0/ch09-appendix.md
---
# 【第 9 章】附录

## 1. 小小白白 Linux 基础命令索引

|   编号   | 命令名称            | 命令说明                     |                  出现篇章                  |
| :------: | :------------------ | :--------------------------- | :----------------------------------------: |
| `cmd-01` | `apt update`        | 查询软件更新                 |      [《远程登录篇》](./ch03-ssh.md)       |
| `cmd-02` | `apt upgrade`       | 执行软件更新                 |      [《远程登录篇》](./ch03-ssh.md)       |
| `cmd-03` | `nano`              | 文本编辑器                   |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-04` | `systemctl restart` | 重启某个服务                 |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-05` | `adduser`           | 给系统新增用户               |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-06` | `apt install`       | 安装某个软件                 |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-07` | `visudo`            | 修改 sudo 权限设置专用编辑器 |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-08` | `sudo`              | 用`root`权限运行某个命令     |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-09` | `chmod`             | 修改目标文件/文件夹的权限    |    [《安全防护篇》](./ch04-security.md)    |
| `cmd-10` | `mkdir`             | 新建文件夹                   |    [《网站建设篇》](./ch05-webpage.md)     |
| `cmd-11` | `systemctl reload`  | 重新加载某个服务             |    [《网站建设篇》](./ch05-webpage.md)     |
| `cmd-12` | `wget`              | 访问（或下载）某个网页文件   |  [《证书管理篇》](./ch06-certificates.md)  |
| `cmd-13` | `acme.sh`           | acme.sh 证书管理相关的命令   |  [《证书管理篇》](./ch06-certificates.md)  |
| `cmd-14` | `rm`                | 删除命令                     | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `cmd-15` | `touch`             | 建立空白文件                 | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `cmd-16` | `systemctl`         | `systemd`基本服务管理命令    | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `cmd-17` | `reboot`            | 重启 Linux 系统              | [《Xray 服务器篇》](./ch07-xray-server.md) |

## 2. 小小白白 Linux 重要配置文件索引

|   编号    | 配置文件位置                | 文件说明                   |                  出现篇章                  |
| :-------: | :-------------------------- | :------------------------- | :----------------------------------------: |
| `conf-01` | `/etc/ssh/sshd_config`      | SSH 远程登录程序设置       |      [《远程登录篇》](./ch03-ssh.md)       |
| `conf-02` | `/etc/nginx/nginx.conf`     | Nginx 程序设置             |    [《网站建设篇》](./ch05-webpage.md)     |
| `conf-03` | `/etc/sysctl.d/99-bbr.conf` | 自定义 kernel 参数配置文件 | [《Xray 服务器篇》](./ch07-xray-server.md) |

## 3. 小小白白 Xray 重要文件索引

|   编号    | 配置文件位置                         | 文件说明      |                  出现篇章                  |
| :-------: | :----------------------------------- | :------------ | :----------------------------------------: |
| `xray-01` | `/usr/local/etc/xray/config.json`    | Xray 程序设置 | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `xray-02` | `/home/vpsadmin/xray_cert/xray.cert` | TLS 证书      | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `xray-03` | `/home/vpsadmin/xray_cert/xray.key`  | TLS 私钥      | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `xray-04` | `/home/vpsadmin/xray_log/access.log` | Xray 访问日志 | [《Xray 服务器篇》](./ch07-xray-server.md) |
| `xray-05` | `/home/vpsadmin/xray_log/error.log`  | Xray 错误日志 | [《Xray 服务器篇》](./ch07-xray-server.md) |

---

---
url: /document/level-1.md
---
# 入门技巧

**这个章节是入门级的 Xray 使用心得分享，主要分享一些 Xray 常用功能模块的原理说明。**

[回落 (fallbacks) 功能简析](./fallbacks-lv1.md)

[路由 (routing) 功能简析（上）](./routing-lv1-part1.md)

[路由 (routing) 功能简析（下）](./routing-lv1-part2.md)

[Xray 的工作模式简析](./work.md)

[通过 SNI 回落功能实现伪装与按域名分流](./fallbacks-with-sni.md)

[通过 DNS 模块实现精准的境内外分流](./routing-with-dns.md)

---

---
url: /document/level-1/fallbacks-lv1.md
---
# 回落 (fallbacks) 功能简析

在使用 Xray 的过程中，你一定无数次的听说了【回落】这个功能。本文就稍微说明一下这个功能的逻辑以及使用方式。

## 1. 回顾《小小白白话文》中的回落

如果你用了《小小白白话文》中的[Xray 配置](../level-0/ch07-xray-server.md#_7-4-配置xray)，并完成了[HTTP 自动跳转 HTTPS 优化](../level-0/ch07-xray-server.md#_7-8-服务器优化之二-开启http自动跳转https)，那么你已经有了基于 `VLESS` 协议的简易回落：

```json
{
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "users": [
          // ... ...
        ],
        "decryption": "none",
        "fallbacks": [
          {
            "dest": 8080 // 默认回落到防探测的代理
          }
        ]
      },
      "streamSettings": {
        // ... ...
      }
    }
  ]
}
```

这一段配置用人话要怎么解释呢？

1. **`Xray` 的入站端口 `[inbound port]` 是 `443`**

   即由 `Xray` 负责监听 `443` 端口的 `HTTPS` 流量

2. **`Xray` 的入站协议 `[inbound protocol]` 是 `vless`**

   只有 `vless` 协议的流量才会流入 `Xray` 中做后续处理。

   ::: warning
   **注：** `VLESS` 这个轻量协议开发的初衷就是给 `xray` 及 `v2fly` 等核心引入回落功能、并同时减少冗余校验/加密。（当然，到目前为止，`xray` 中的 `trojan` 协议也已完整支持回落功能。）
   :::

3. **回落目标端口 `[fallback dest]` 是 `8080`**

   `Xray` 接受 `443` 端口的访问流量后，属于 `vless` 协议的流量、由 `Xray` 进行内部处理并转发至出站模块。而其他非 `vless` 协议的流量，则转发至 `8080` 端口。

   ::: warning
   **问：到底是单数还是复数？**

   答：一定有聪明的同学发现，配置文件中，明明是复数 `inbounds`, `fallbacks`，为什么我解释的时候都是单数：`inbound`, `fallback` 呢？

   因为，配置文件中用复数，说明 `xray` 支持 N 个同等级的元素（即 N 个入站，M 个回落等等），上面的示例解析中仅仅是其中一个，所以我用了单数。
   :::

4. **回落给 `8080` 端口的流量，由后续程序处理**

   小小白白话文中的示例，就是 `8080` 端口由 `Nginx` 处理，根据配置找到并展示小熊猫的网页。

5. **总结，小小白白话文示例中的最简单回落，完整数据路线如下：**

   ```mermaid
   graph LR;

   W(外部 HTTP:80 请求) --> N80(HTTP:80)

   subgraph Nginx 外部监听
   N80 -.- N301(301转写) -.- N443(HTTPS:443)
   end

   N443 --> X(Xray 监听 443) .- X1{入站判断}
   X1 --> |接收 VLESS 流量| X2(Xray内部规则)
   X2 --> O(Xray Outbounds 出站)
   X1 ==> |回落 非VLESS 流量| N8080(Nginx:8080)
   N8080:::nginxclass ==> H(index.html)

   H:::nginxclass
   classDef nginxclass fill:#FFFFDE

   ```

## 2. 重新认识回落 (WHAT, HOW `v1`)

基于上面的示例，你应该就可以明白什么是回落（What）和怎么回落（How）了，简单地说就是下面这几个要素：

1. 回落的时间是流量进入 `Xray监听端口` 后
2. 回落的依据是 `协议类型` 等流量特征
3. 回落的目标是某个 `端口`
4. 被回落的流量由监听 `回落端口` 的后续程序接手

## 3. 为什么要回落 (WHY `v1`)

最初，是为了防御 **【主动探测】** (Active Probing)

**主动探测：** 简单粗暴的理解，就是指外部通过发送特定的网络请求，并解读服务器的回应内容，来推测服务器端是否运行了 `xray`, `v2fly`, `shadowsocks` 等代理工具。一旦可以准确认定，则服务器可能受到干扰或阻断。

之所以可以根据服务器回应内容进行解读，就是因为一次完整的数据请求，其实有很多数据交换的步骤，每一个步骤，都会产生一些软件特征。用大白话说就是：

* 正常的网站的回应，一定【会有】类似 `Nginx`, `Apache`, `MySQL` 的 Web 服务、数据库等工具的特征
* 正常的网站的回应，一定【不会有】类似 `xray`, `v2fly`, `shadowsocks` 等代理工具的特征

于是，当我们给 `Xray` 提供了【回落】功能后（如上例，回落给 `Nginx`），面对任何用来探测的请求，产生的结果是：

* 探测流量无法掌握你的 `VLESS` 要素，故都会被回落至 `Nginx`
* 探测流量全都回落进入 `Nginx` ，故 VPS 服务器的回应一定【会有】 `Nginx` 的特征
* 因为 `Xray` 本身不对探测流量做任何回应 ，所以 VPS 的回应一定【不会有】 `Xray` 的特征

至此，【回落】功能就从数据交互逻辑上解决了服务器被 **【主动探测】** 的安全隐患。

## 4. 重新认识【回落の完全体】 (WHAT, WHY, HOW `v2`)

为什么又要再次认识回落呢？ 因为，上面仅仅说清楚了基于“协议”的、抵抗【主动探测】的初版回落。

在 [RPRX](https://github.com/rprx) 不断开发迭代 `VLESS` 协议及 `fallback` 功能的过程中，逐渐发现，回落完全可以更加灵活强大，只要在保证抵抗【主动探测】的前提下，充分利用数据首包中的信息，其实可以做到多元素、多层次的回落。（如 `path`, `alpn` 等）

基于这个开发理念，【回落】功能才逐渐成长为现在的完全体，即完成了 `纯伪装 --> ws分流 --> 多协议多特征分流` 的进化。最终版甚至完全替代了以前要用 Web 服务器、其他工具才能完成的分流的功能。且由于上述的【回落/分流】处理都在首包判断阶段以毫秒级的速度完成、不涉及任何数据操作，所以几乎没有任何过程损耗。

**因此，现在 `Xray` 中【完整体的回落功能】，同时具备下述属性：**

* **安全：** 充分抵御主动探测攻击
* **高效：** 几乎毫无性能损失
* **灵活：** 数据灵活分流、常用端口复用（如 443）

::: tip 啰嗦君
这样多轮介绍虽然略显繁琐，但只有这样层层深入展开，才能充分的说明【回落の完全体】独有的强大！
:::

## 5. 多层回落示例及解读

理解了【回落の完全体】是什么，那就可以动手操作配置多层回落了。

### 5.1 首先，我将服务器端配置的 443 监听段摘抄如下：

```json
{
  "port": 443,
  "protocol": "vless",
  "settings": {
    "users": [
      {
        "id": "", // 填写你的 UUID
        "flow": "xtls-rprx-vision",
        "level": 0,
        "email": "love@example.com"
      }
    ],
    "decryption": "none",
    "fallbacks": [
      {
        "dest": 1310, // 默认回落到 Xray 的 Trojan 协议
        "xver": 1
      },
      {
        "path": "/websocket", // 必须换成自定义的 PATH
        "dest": 1234,
        "xver": 1
      },
      {
        "path": "/vmesstcp", // 必须换成自定义的 PATH
        "dest": 2345,
        "xver": 1
      },
      {
        "path": "/vmessws", // 必须换成自定义的 PATH
        "dest": 3456,
        "xver": 1
      }
    ]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "tls",
    "tlsSettings": {
      "alpn": ["http/1.1"],
      "certificates": [
        {
          "certificateFile": "/path/to/fullchain.crt", // 换成你的证书，绝对路径
          "keyFile": "/path/to/private.key" // 换成你的私钥，绝对路径
        }
      ]
    }
  }
}
```

这一段配置用人话要怎么解释呢？

1. **`Xray` 的入站端口 (`inbound port`) 是 `443`**

   即由 `Xray` 负责监听 `443` 端口的 `HTTPS` 流量，并使用 `certificates` 项下设定的 `TLS` 证书来进行验证

2. **`Xray` 的入站协议 (`inbound protocol`) 是 `vless`**

   `vless` 协议流量直接流入 `Xray` 中做后续处理

3. **非 `VLESS` 协议流量有 4 个不同的回落目标：**
   1. `path` 为 `websocket` 的流量，回落给端口 `1234` 后续处理
   2. `path` 为 `vmesstcp` 的流量，回落给端口 `2345` 后续处理
   3. `path` 为 `vmessws` 的流量，回落给端口 `3456` 后续处理
   4. 其它所有流量，回落给端口 `1310` 后续处理

4. **`xver` 为 `1` 表示开启 `proxy protocol` 功能，向后传递来源真实 IP**

5. **上述回落结构如下图所示：**

   ```mermaid
   graph LR;

   W443(外部 HTTP:443 请求) --> X443(Xray-inbound: 443) .- X1{入站判断}
   X1 --> |协议 = VLESS 的流量| X2(Xray内部规则)
   X2 --> O(Xray Outbounds 出站)

   X1 --> |path = /websocket 的流量| X1234(Xray-inbound:1234)
   X1 --> |path = /vmesstcp 的流量| X2345(Xray-inbound:2345)
   X1 --> |path = /vmessws 的流量| X3456(Xray-inbound:3456)
   X1 --> |其它所有流量| X1310(Xray-inbound:1310)

   ```

6. **网页回落不见了！**

   没错，聪明的同学应该发现了，防御【主动探测】的 `nginx回落` 不见了！！！这是为什么呢？会不会不安全？别急，我们继续分析：

### 5.2 后续监听处理的配置段摘抄如下：

1. 后续处理回落至 `1310` 端口的流量，按照下面的配置验证、处理：

   ```json
   {
     "port": 1310,
     "listen": "127.0.0.1",
     "protocol": "trojan",
     "settings": {
       "users": [
         {
           "password": "", // 填写你的密码
           "level": 0,
           "email": "love@example.com"
         }
       ],
       "fallbacks": [
         {
           "dest": 80 // 或者回落到其它也防探测的代理
         }
       ]
     },
     "streamSettings": {
       "network": "tcp",
       "security": "none",
       "tcpSettings": {
         "acceptProxyProtocol": true
       }
     }
   }
   ```

   看，神奇的事情发生了， `trojan` 协议这里又出现了一个新的 `fallbacks`。前面已经说过，`xray` 中的 `trojan` 协议也具有完整的回落能力，所以，此时 `trojan` 协议可以再次做判断和回落（这也就是传说中的套娃回落了）：

   * 所有 `trojan` 协议的流量，流入 `Xray` 中做后续处理
   * 所有非 `trojan` 协议的流量，转发至 `80` 端口，【主动探测】的防御，完成！

2. 后续处理回落至 `1234` 端口的流量，仔细看！它其实是 `vless+ws`：

   ```json
   {
     "port": 1234,
     "listen": "127.0.0.1",
     "protocol": "vless",
     "settings": {
       "users": [
         {
           "id": "", // 填写你的 UUID
           "level": 0,
           "email": "love@example.com"
         }
       ],
       "decryption": "none"
     },
     "streamSettings": {
       "network": "ws",
       "security": "none",
       "wsSettings": {
         "acceptProxyProtocol": true, // 提醒：若你用 Nginx/Caddy 等反代 WS，需要删掉这行
         "path": "/websocket" // 必须换成自定义的 PATH，需要和分流的一致
       }
     }
   }
   ```

3. 后续处理回落至 `2345` 端口的流量，仔细看！它其实是 `vmess直连`：

   ```json
   {
     "port": 2345,
     "listen": "127.0.0.1",
     "protocol": "vmess",
     "settings": {
       "users": [
         {
           "id": "", // 填写你的 UUID
           "level": 0,
           "email": "love@example.com"
         }
       ]
     },
     "streamSettings": {
       "network": "tcp",
       "security": "none",
       "tcpSettings": {
         "acceptProxyProtocol": true,
         "header": {
           "type": "http",
           "request": {
             "path": [
               "/vmesstcp" // 必须换成自定义的 PATH，需要和分流的一致
             ]
           }
         }
       }
     }
   }
   ```

4. 后续处理回落至 `3456` 端口的流量，再仔细看！它其实是是 `vmess+ws(+cdn)`。

   ::: warning 说明
   你没看错，这就是 v2fly 曾经推荐的组合之一，并可完整支持 `CDN`。现已加入完美回落套餐哦！
   :::

   ```json
   {
     "port": 3456,
     "listen": "127.0.0.1",
     "protocol": "vmess",
     "settings": {
       "users": [
         {
           "id": "", // 填写你的 UUID
           "level": 0,
           "email": "love@example.com"
         }
       ]
     },
     "streamSettings": {
       "network": "ws",
       "security": "none",
       "wsSettings": {
         "acceptProxyProtocol": true, // 提醒：若你用 Nginx/Caddy 等反代 WS，需要删掉这行
         "path": "/vmessws" // 必须换成自定义的 PATH，需要和分流的一致
       }
     }
   }
   ```

5. 至此，我们就能够完整的画出模板的回落路线了：

```mermaid
    graph LR;

    W443(外部 HTTP:443 请求) --> X443(Xray-inbound: 443) .- X1{入站判断}
    X1 --> |协议 = VLESS 的流量| X2(Xray内部规则)
    X2 --> XO(Xray Outbounds 出站)

    X1 --> |path = /websocket 的流量| X1234(Xray-inbound:1234)
    X1 --> |path = /vmesstcp 的流量| X2345(Xray-inbound:2345)
    X1 --> |path = /vmessws 的流量| X3456(Xray-inbound:3456)
    X1 --> |其它所有流量| X1310(Xray-inbound:1310)

    X1234 --> X2
    X2345 --> X2
    X3456 --> X2

    X1310 --> |协议 = trojan 的流量| X2
    X1310 --> |其他所有流量| N80(Nginx:80)

    N80:::nginxclass --> H(index.html)

    H:::nginxclass
    classDef nginxclass fill:#FFFFDE
```

## 6. 结语

至此，`Xray` 的【回落】功能就介绍完了。希望本文能够对你理解 `Xray` 的强大有所帮助。

## 7. 附加题

我再无耻的留一个附加题：本文详解的 [VLESS-TCP-XTLS-WHATEVER](https://github.com/XTLS/Xray-examples/blob/main/VLESS-TCP-XTLS-WHATEVER/) 模板？是否有可以优化的地方？

提示：HTTP 自动跳转 HTTPS

---

---
url: /document/level-1/routing-lv1-part1.md
---
# 路由 (routing) 功能简析（上）

如果说 Xray 的【强大】主要体现在它极致的速度和广泛的兼容性。那么 Xray 的【灵活】，则主要应该归功于它巧妙的【路由】功能。本文就稍微说明一下这个功能的逻辑以及使用方式。

## 1. 初识【路由】三兄弟

要理解路由，就要理解完整的路由功能需要有三兄弟来合力完成：1. **入站**；2. **路由**；3. **出站**。

![路由三兄弟](./routing-lv1-img01-trio.jpg)

三兄弟桃园结义，不求同年同月同日生，但求同年同月同日死。

所以谨记：任何一个元素错误，就可能导致路由功能无法正常工作。

因为路由的灵活性非常高，只看技术文档很容易把自己绕晕，所以本文我们用几个具体的示例来逐层讲解。

::: warning 啰嗦君
路由功能实在过于灵活，所以本文的示例，都是为了讲解对应的概念，实际使用时请根据自己的需求进行调整。
:::

## 2. 基本功： “兄弟一条心”

下图的示例，就是在客户端的 `Xray` 入站接收 APP 数据、在路由 100%转发给出站，并从出站流向 VPS。

```mermaid
    graph LR;

    S(APP数据) .-> I[入站]

    subgraph Xray
    I --> R[路由] --> O[出站]
    end

    O .-> V(VPS)

    V:::greyclass
    S:::greyclass
    R:::routingclass
    classDef greyclass fill:#C0C0C0
    classDef routingclass fill:#FFFFDE

```

下面我们来逐个分析：

### 2.1 入站

::: tip
**入站：** 就是流量如何流入 `Xray`
:::

下面的入站配置示例，用大白话说就是：数据按照 `socks` 协议，通过 `10808` 端口，从本机 `127.0.0.1` 流入`Xray`。同时，`Xray` 将这个入站用 `[tag]` 命名为 `inbound-10808`。

```json
{
  "inbounds": [
    {
      "tag": "inbound-10808",
      "protocol": "socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "settings": {
        "udp": true
      }
    }
  ]
}
```

**2.2 出站**

::: tip
**出站：** 就是流量如何流出 `Xray`
:::

下面的出站配置示例，用大白话说就是：数据按照 `VLESS` 协议，以 `tcp + xtls` 的方式、及其他相关设置，把流量发送给对应的 VPS。同时，`Xray` 将这个出站用 `[tag]` 命名为 `proxy-out-vless`：

```json
{
  "outbounds": [
    {
      "tag": "proxy-out-vless",
      "protocol": "vless",
      "settings": {
        "address": "a-name.yourdomain.com",
        "port": 443,
        "id": "uuiduuid-uuid-uuid-uuid-uuiduuiduuid",
        "flow": "xtls-rprx-vision",
        "encryption": "none",
        "level": 0
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "serverName": "a-name.yourdomain.com",
          "allowInsecure": false,
          "fingerprint": "chrome"
        }
      }
    }
  ]
}
```

### 2.3 路由

::: tip
**路由：** 就是把【入站】和【出站】之间的通道，用某种【条件】串联起来
:::

下面的路由配置示例，用大白话说就是：把所有通过 `[tag]="inbound-10808"` 入站流入 `Xray` 的流量，`100%` 全部流转导入 `[tag]="proxy-out-vless"` 的出站，没有任何分流或其他操作。

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "inboundTag": ["inbound-10808"],
        "outboundTag": "proxy-out-vless"
      }
    ]
  }
}
```

至此，我们最开始设计的极简规则【客户端的 `Xray` 入站接收 APP 数据、在路由 100%转发给出站，并从出站流向 VPS】已经完成。

### 2.4 路由配置项解析之一：流量筛选的依据

注意观察路由配置，我们可以看到几个新名词：

1. "domainStrategy": "AsIs"
2. “rules”
3. "inboundTag": \["inbound-10808"]
4. "outboundTag": "proxy-out-vless"

其中 `domainStrategy` 我们暂且按下不表，先简单说明后面几个：

|    配置名称     |                                                                                        配置值                                                                                         | 配置说明                                                                                                         |
| :-------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------- |
|    `“rules”`    |                                                     | 它的内层就是【路由规则】的明细设置                                                                               |
| `"inboundTag"`  |                                                                                  `["inbound-10808"]`                                                                                  | 筛选流量的 **【依据】** 是【入站 Tag】，具体 **【条件】** 现在只有一个：【入站来源是 `inbound-10808`】           |
| `"outboundTag"` |                                                                                  `"proxy-out-vless"`                                                                                  | 当上面的筛选条件成立时（即入站`[tag]="inbound-10808"`时 ），`Xray` 会将流量导入 `[tag]="proxy-out-vless"` 的出站 |

本例中，我们只有一个入站，它的`"inboundTag" = "inbound-10808"` 。我们也只有一个出站，它的 `[tag]="proxy-out-vless"`。所以根据上面这个路由规则，从唯一入站端口 `10808` 流入`Xray`的流量，`100%` 符合筛选条件、会被路由模块选中，然后转发给唯一的出站。

至此，**入站**、**路由**、**出站** 三兄弟就已经可以携手工作了。当然，现在这个 100%转发的工作并没有什么特别的意义。那么接下来，我们就看看这种分工合作的机制可以带来什么好处。

## 3. 小试牛刀： “三分天下” 之 “域名分流”

> `[geosite.dat]`

```mermaid
    graph LR;

    S(APP数据) .-> I[入站]

    subgraph Xray
    I --> R[路由] -- "geosite:category-ads-all" --> O1[block]
    R[路由] -- "geosite:cn" --> O2[direct]
    R[路由] -- "geosite:geolocation-!cn" --> O3[proxy]

    end

    O2 .-> D(国内服务器)
    O3 .-> V(VPS)

    O1:::redclass
    V:::greyclass
    S:::greyclass
    R:::routingclass

    classDef redclass fill:#FF0000
    classDef greyclass fill:#C0C0C0
    classDef routingclass fill:#FFFFDE,stroke:#000000

```

这个配置逻辑，其实就是最简单、最常用的（《小小白白话文》中也在用的）路由配置三件套：

1. 广告流量屏蔽 `[block]`
2. 国内流量直连 `[direct]`
3. 国外流量转发 VPS `[proxy]`

::: warning 注意
小小白白话文中的直连配置是包括【国内域名】、【国内 IP】、【本机内部 IP】的。这里先讲解【国内域名】。
:::

### 3.1 入站

保持上例的 `inbound-10808` 不变。

### 3.2 出站

在上例的基础上，我们已经有了 `[proxy]` 的出站 `"proxy-out-vless"`，所以它保持不变。显而易见，我们需要加入两个新的出站方式：`[block]` 和 `[direct]`，如下：

```json
{
  "outbounds": [
    {
      "tag": "proxy-out-vless"
      // ... ...
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    },
    {
      "tag": "direct-out",
      "protocol": "freedom"
    }
  ]
}
```

上面的配置用大白话翻译如下：

1. 上例中的 `[proxy-out-vless]` 出站配置保持不变
2. 加入 **`blackhole` 黑洞协议**，通过这个协议出站的流量，其实都被发送到了 `Xray` 内部的黑洞里，再也无法逃脱，于是效果就是屏蔽 `[block]`
3. 加入 **`freedom` 自由协议**，通过这个协议出站的流量，是自由的离开`Xray`去寻找原定的服务器，就像从没有来过，于是效果就是直连 `[direct]` （我这里起名叫做 `[direct-out]` 是为了强调它是一个出站）

### 3.3 路由

接下来就是见证奇迹的时刻了，我们可以用【路由】的配置把这些连接起来！

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "domain": ["geosite:cn"],
        "outboundTag": "direct-out"
      },
      {
        "domain": ["geosite:geolocation-!cn"],
        "outboundTag": "proxy-out-vless"
      }
    ]
  }
}
```

为了理解这个配置文件，我们要稍微解释一下这里出现的几个新配置项：

* `"domain": ["geosite:category-ads-all"]`
* `"domain": ["geosite:cn"]`
* `"domain": ["geosite:geolocation-!cn"]`

### 3.4 简析域名文件： `geosite.dat`

其实，聪明的你大概可以通过这些配置项的名称猜出来个大概：

* `"domain"`：就是这次筛选流量的 **【依据】** 是 **【域名】** （而不再是入站 tag）
* `"geosite"`：就是 `Xray` 会去 `geosite.dat` 文件中寻找 **【符合条件的域名】**
* `"category-ads-all"`：就是该文件中的 **【所有广告类域名】**
* `"cn"`：就是该文件中的 **【中国域名】**
* `"geolocation-!cn"`：就是该文件中的 **【非中国域名】**

结合这些说明，3.3 中的配置用大白话翻译就是：

1. APP 试图访问国外域名 `"domain": "geolocation-!cn"` 的流量，通过 `[proxy-out-vless]` 出站，转发至 VPS
2. APP 试图访问国外域名广告域名 `"domain": "geosite:category-ads-all"` 的流量，通过 `[block]` 出站，转发至黑洞进行屏蔽
3. APP 试图访问国内域名 `"domain": "geosite:cn"` 的流量，通过 `[direct-out]` 出站，自由离开完成直连

这时，才让【路由功能】的好处稍微得到了一些展现。

### 3.5 所以 `geosite.dat` 到底是什么？不是有个 `GFWList` 吗？

你想，这世界上的域名何止千万，如果我们每写一个基于【域名】匹配的路由规则，都要自己收集、手动输入域名，那效率将会何其低下！

而如果所有的域名都只有一个种类，`[direct], [proxy], [block]` 只能三选其一，那又是多么的不方便！

就如关羽需要他的青龙偃月刀，`geosite.dat` 文件便作为【路由功能】驱使的神兵利器横空出世了，它致力于为用户提供成熟完善的【域名分类表】。让用户可以简单的通过 `geosite:xxx` 这种格式方便的调用任何子类，定制符合自身需求的路由规则。

这种模块化结构提供的灵活性，其实远超传统的一揽子防火墙域名列表 [`GFWList`](https://github.com/gfwlist/gfwlist)。为什么这么说呢？比如，你可以指定苹果的域名 `geosite:apple` 和 icloud 相关域名 `geosite:icloud` 通过代理 `[proxy]`，但是苹果的软件域名 `geosite:apple-update` 保持直连 `[direct]` 来保持最大下载速度。

::: warning
**注意：** 现在，`geosite.dat` 文件其实有多种选择：

最初，从 `Victoria Raymond` 主力维护 `Project V` 项目时期，便提供了最初的配套项目：[`domain-list-community`](https://github.com/v2ray/domain-list-community)，用来收集、沉淀、分类各种常用的域名类型；

之后，随着 V 姐突然消失导致 `Project V` 的原项目开发陷入停滞，`v2fly` 社区维护并持续更新了社区版本的 [`domain-list-community`](https://github.com/v2fly/domain-list-community)；

同时，Loyalsoldier 维护了其个人修改增强的路由规则文件 [v2ray-rules-dat](https://github.com/Loyalsoldier/v2ray-rules-dat)，提供了诸多不同的选择和分类逻辑；

另外，`Project X` 也计划于未来定制维护更适合 `Xray` 使用的路由规则文件 [Xray-rules-dat](https://github.com/XTLS/Xray-rules-dat)。~~(你们看，文件夹都建好了，所以快了快了)~~

甚至，你还可以定制自己的 `geosite` 文件，外挂给 `Xray` 使用，但是这个就跑题了，本文不展开。

如果你发现有些你遇到的域名没有被合理分类，请向上面的项目们提出 `issue` 甚至提交 `Pull Request` 吧！社区列表社区维护，人人为我我为人人！

:::

### 3.6 军师锦囊藏奇兵：一条隐藏的路由规则

事实上，当你认真思考上面的规则，不难发现一个问题，我们的所有规则都只规定了【当入站流量 **符合某种条件时** 应该被转发给哪个出站】，那么，如果 `geosite.dat` 文件不全面，我们的入站流量【**不符合任何条件时**】，`Xray` 会怎么处理呢？

::: warning 注意
如果你认为【不符合条件当然就无法连接啦！】的话，你可要重新思考一下哦。因为只有指定了 `[block]` 规则，才会被导入到 `blackhole` 黑洞协议从而阻断连接
:::

事实上，`Xray` 为了避免路由规则不完全导致的规则混乱，已经贴心的提供了一条隐藏的路由规则：【**当入站流量不符合任何条件时，转发给第一个出站** 】

这样，就不会有任何流量被漏掉了。所以，你一定要把你最信赖的心腹大将放在【第一条出站】，让它为你守城护池。

### 3.7 再看“三分天下”的大地图

因为我们在前面的示例中把 `[proxy-out-vless]` 放在了出站的第一位，所以隐藏规则生效时，流量会通过 `VLESS` 协议被转发至远端的 VPS。因此，`Xray` 此时的完整工作逻辑如下：

```mermaid
    graph LR;

    S(APP数据) .-> I[入站]

    subgraph Xray
    I --> R[路由] -- "geosite:category-ads-all" --> O1[block]
    R[路由] -- "geosite:cn" --> O2[direct]
    R[路由] -- "geosite:geolocation-!cn" --> O3[proxy]
    R[路由] -. "没有命中规则的流量" .-> O4[第一条出站]

    end

    O2 .-> D(国内服务器)
    O3 .-> V(VPS)
    O4 .-> V(VPS)

    O1:::redclass
    V:::greyclass
    S:::greyclass
    R:::routingclass

    classDef redclass fill:#FF0000
    classDef greyclass fill:#C0C0C0
    classDef routingclass fill:#FFFFDE,stroke:#000000

```

事实上，这就是传统所谓的 **【默认科学上网、国内网站白名单直连】** 的配置。

## 4. “三分天下” 之 “蜀魏争雄”

现在，你已经知道了隐藏的默认路由规则：【**当入站流量不符合任何条件时，转发给第一个出站** 】。这时候，你应该能看出来，究竟是【科学上网】为王，还是【直连】称霸，全看你的第一条出站是什么！

上一步我们已经配置出了 **【默认科学上网、国内网站白名单直连】** 的规则。那么现在只要 **【把直连规则放在第一位】**，就立即变成了正好相反的 **【默认直连、国外网站白名单科学上网】** 规则。

是不是，非常地简单？

```json
{
  "outbounds": [
    {
      "tag": "direct-out",
      "protocol": "freedom"
    },
    {
      "tag": "proxy-out-vless"
      // ... ...
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ]
}
```

此时，路由规则其实变成了：

```mermaid
    graph LR;

    S(APP数据) .-> I[入站]

    subgraph Xray
    I --> R[路由] -- "geosite:category-ads-all" --> O1[block]
    R[路由] -- "geosite:geolocation-!cn" --> O3[proxy]
    R[路由] -- "geosite:cn" --> O2[direct]
    R[路由] -. "没有命中规则的流量" .-> O4[第一条出站]

    end

    O2 .-> D(国内服务器)
    O3 .-> V(VPS)
    O4 .-> D

    O1:::redclass
    V:::greyclass
    S:::greyclass
    R:::routingclass
    classDef redclass fill:#FF0000
    classDef greyclass fill:#C0C0C0
    classDef routingclass fill:#FFFFDE,stroke:#000000

```

这就是路由功能的灵活之处了，你可以自由的改变它的顺序来实现不同的设计。

至此，我们已经解释完了 **【如何利用 `geosite.dat` 文件，通过路由规则，根据【域名】来分流网络流量】。**

## 5. 攻城略池 - 多种路由匹配条件

请确保你已经读懂了上面的内容，因为这样，你就已经理解了【路由】功能的工作逻辑。有了这个基础，我们就可以继续分析【路由】功能更多更详细的配置方式和匹配条件了。

等你看完后面的内容，就完全可以自由的定制属于自己的路由规则啦！还等什么，让我们一起进入 [《路由 (routing) 功能简析（下）》](./routing-lv1-part2.md) 吧！

---

---
url: /document/level-1/routing-lv1-part2.md
---
# 路由 (routing) 功能简析（下）

欢迎继续学习 `Xray` 的【路由】功能！

在 [《路由 (routing) 功能简析（上）》](./routing-lv1-part1.md) 中，我们已经对【路由】功能的工作逻辑有了清晰的理解，也基于 `geosite.dat` 文件做了简单的域名分流配置。

如前面所说，域名分流仅仅是【路由】功能的牛刀小试而已。下面就让我们来看看除了域名之外，还什么可以用做分流依据的东西吧！

## 5. 攻城略池 - 多种路由匹配条件

> `[域名], [IP], [协议], etc.`

基于域名的分流，已经可以让我们对网络流量进行基本合理的分流。为什么说【基本合理】呢？

因为【三分天下】虽然是正确的战略方向，但如果只用【域名】来实现这个战略，其实漏洞百出，比如：

1. 我读了《小小白白话文》后，给 VPS 新申请了一个 `proxy.yourdomain.com` 的域名, 我希望它无论如何都代理，`geosite.dat` 里面有吗？
2. 如果我还有个 `direct.yourdomain.com` 的域名，我希望它无论如何都直连， `geosite.dat` 里面有吗？
3. 本机 `127.0.0.1` 的内部流量，是否正确直连了？（比如 `docker` 等）
4. 路由器、本地局域网 `192.168.*.*` 的流量，是否正确直连了？（比如路由器、群晖等）
5. 我的国内 DNS 查询（如 `223.5.5.5`）是否正确直连了？
6. 我的国外 DNS 查询（如 `1.1.1.1`）是否正确代理了？
7. 其他类似国内公共 DNS 一样没有域名、只有 IP 地址的国内网站，是否正确直连了？
8. 其他类似国外公共 DNS 一样没有域名、只有 IP 地址的国外网站，是否正确代理了？
9. BT 下载的流量，虽然来源是国外，但如果通过 VPS 下载很可能导致违规使用被封，这该如何强制直连？
10. ......

我之所以说只用【域名分流】会漏洞百出，是因为 `geosite.dat` 文件内只包含了一部分常用的域名。换言之，仅仅依赖它，则会：

* 无法匹配文件里没有的新域名
* 无法匹配基于 IP 地址的规则
* 无法匹配基于网络协议的规则

::: warning 啰嗦君
那我们来复习一下，当上面这些情况无法匹配时，会发生什么？对了，会触发隐藏路由规则，即【**转发给第一个出站** 】。这其实就是说：

* 当你的第一个出站是 `[direct-out]` 时：**需要直连的都正确了，但需要代理的则都错误**
* 当你的第一个出站是 `[proxy-out-vless]` 时：**需要代理的都正确了，但需要直连的则都错误**
  :::

所以，我们需要一个办法，让我们鱼与熊掌兼得。这样的办法是否存在呢？**当然存在！** 我们需要的只是【域名】之外更多的【**分流判断依据**】而已。

### 5.1 基于指定域名分流：`[domain], [full]` 等

1. 如果需要匹配某个子域名，如 `a-name.yourdomain.com`，我们使用 `full: "a-name.yourdomain.com"`
2. 前面的 `问题1` 和 `问题2`，就可以通过给 `proxy.yourdomain.com` 指定 `[proxy-out-vless]` 出站，给 `direct.yourdomain.com` 指定 `[direct-out]` 出站来解决
3. 如果需要匹配 `yourdomain.com` 的所有子域名，我们使用 `domain: "yourdomain.com"` 实现
4. 上述两个可以成为两个独立的路由规则，达到某些子域名直连，其他子域名代理的配置
5. 另外，`[domain]` 还支持正则表达式等匹配方式。详情请参考 [《基础配置模块 - 路由》文档](../../config/routing.md)

上述配置如下：

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      // 指定子域名直连
      {
        "domain": ["full:direct.yourdomain.com"],
        "outboundTag": "direct-out"
      },
      // 指定子域名转发VPS
      {
        "domain": ["full:proxy.yourdomain.com"],
        "outboundTag": "proxy-out-vless"
      },
      // 指定泛域名转发VPS
      {
        "domain": ["yourdomain.com"],
        "outboundTag": "proxy-out-vless"
      }
    ]
  }
}
```

### 5.2 基于 IP 文件分流：`geoip.dat`

与 `geosite.dat` 规则文件十分类似的，我们还有 `geoip.dat` 这个规则文件，它致力于为用户提供成熟完善的【IP 分类表】。让用户可以简单的通过 `geoip:xxx` 这种格式方便的调用任何子类，定制符合自身需求的路由规则 。

1. 解决前面的 `[问题3], [问题4]`，我们使用 `geoip:private` 类别来指定 `[direct-out]`
2. 解决前面的 `[问题7]`，我们使用 `geoip:cn` 类别来指定 `[direct-out]`
3. 解决前面的 `[问题8]`，由于 `geoip` 中没有【非中国 IP】这个分类（因为这等于要收集全世界的 IP 段），所以我们用隐藏规则代替，也就是将 `[proxy-out-vless]` 放在第一个出站

上述配置如下：

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      // 本机内部地址、局域网地址直连
      {
        "ip": ["geoip:private"],
        "outboundTag": "direct-out"
      },
      // 国内IP集直连
      {
        "ip": ["geoip:cn"],
        "outboundTag": "direct-out"
      }
    ]
  }
}
```

### 5.3 基于指定 IP 地址分流

除了使用 `geoip.dat` 核心自然也支持直接使用IP进行路由以满足各种需求。

1. 解决前面的 `[问题5]`，我们使用 `ip: "223.5.5.5"` 来指定 `[direct-out]`
2. 解决前面的 `[问题6]`，我们使用 `ip: "1.1.1.1"` 来指定 `[proxy-out-vless]`

上述配置如下：

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      // 指定IP地址直连
      {
        "ip": ["223.5.5.5"],
        "outboundTag": "direct-out"
      },
      // 指定IP地址转发VPS
      {
        "ip": ["1.1.1.1"],
        "outboundTag": "proxy-out-vless"
      }
    ]
  }
}
```

### 5.4 基于协议类型分流：`[protocol]` 等

1. 解决前面的 `[问题9]`，我们使用 `"protocol": ["bittorrent"]` 类别来指定 `[direct-out]`

::: tip
你需要打开入站代理中的 `sniffing` 才能使用此种方式分流。
:::

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      // 指定 BT 协议直连
      {
        "protocol": ["bittorrent"],
        "outboundTag": "direct-out"
      }
    ]
  }
}
```

### 5.5 基于更多条件的分流

到目前为止，我们仍然只讲了【路由功能】分流能力的冰山一角！因为它还支持很多其他的判断条件！我在此简单罗列如下：

本文已经讲过的：

* `inboundTag`
* `domain`
* `ip`
* `protocol`

本文尚未讲到的：

* `port`
* `sourcePort`
* `network`
* `source`
* `user`
* `attrs`

但这些内容实在是过多，全部展开就远远不是 `level-1` 的内容了，所以，需要这些复杂条件的朋友，请仔细阅读 [《基础配置模块 - 路由》文档](../../config/routing.md) 自学哦！有问题就去 TG 群里面问问吧！

## 6. “霸业初定”：路由规则整体回顾

到现在为止，我们已经累积出了一套战略雄伟、战术精准的路由规则，为了避免混乱，现在就对它进行一次完整的整理和回顾。

::: warning 注意
路由生效的顺序是：【从上往下，依次判断】，所以我一般推荐的规则顺序是：

`[1-block] --> [2-direct] --> [3-proxy] --> [4-first-outbound]`
:::

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      // [1-block 广告流量屏蔽]
      // 1.1  广告域名集屏蔽
      {
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      // [2-direct 国内流量直连]
      // 2.1 国内域名集、指定子域名直连
      {
        "domain": ["geosite:cn", "full:direct.yourdomain.com"],
        "outboundTag": "direct-out"
      },
      // 2.2 本机内部地址+局域网、国内IP、指定IP直连
      {
        "ip": ["geoip:private", "geoip:cn", "223.5.5.5"],
        "outboundTag": "direct-out"
      },
      // 2.3 BT协议流量直连
      {
        "protocol": ["bittorrent"],
        "outboundTag": "direct-out"
      },
      // [3-proxy 国外流量转发VPS]
      // 3.1 国外域名集、指定子域名、指定泛域名转发VPS
      {
        "domain": [
          "geosite:geolocation-!cn",
          "full:proxy.yourdomain.com",
          "yourdomain.com"
        ],
        "outboundTag": "proxy-out-vless"
      },
      // 3.2 指定IP转发VPS
      {
        "ip": ["1.1.1.1"],
        "outboundTag": "proxy-out-vless"
      }
      // [4-default-routing 第一条出站]
      // 没有匹配到任何规则的流量，默认使用第一条出站处理
    ]
  }
}
```

此时，路由规则其实变成了：

```mermaid
    graph LR;

    S(APP数据) .-> I[入站]

    subgraph Xray
    I --> R[路由] -- "geosite:category-ads-all" --> O1[block]

    R[路由] -- "geosite:cn" --> O2[direct]
    R[路由] -- "direct.yourdomain.com" --> O2[direct]
    R[路由] -- "geoip:private" --> O2[direct]
    R[路由] -- "geoip:cn" --> O2[direct]
    R[路由] -- "ip:223.5.5.5" --> O2[direct]
    R[路由] -- "protocol:bittorrent" --> O2[direct]

    R[路由] -- "geosite:geolocation-!cn" --> O3[proxy]
    R[路由] -- "proxy.yourdomain.com" --> O3[proxy]
    R[路由] -- "*.yourdomain.com" --> O3[proxy]
    R[路由] -- "ip:1.1.1.1" --> O3[proxy]

    R[路由] -. "没有命中规则的流量" .-> O4[第一条出站]

    end

    O2 .-> D(国内服务器)
    O3 .-> V(VPS)

    O1:::redclass
    V:::greyclass
    S:::greyclass
    R:::routingclass
    classDef redclass fill:#FF0000
    classDef greyclass fill:#C0C0C0
    classDef routingclass fill:#FFFFDE,stroke:#000000

```

至于第一条出站是 `[direct-out]` 还是 `[proxy-out-vless]`，这就全看你的需求了。

## 7. 路由配置常见错误

请大家注意看，我上面每一条路由规则，都是一个独立的匹配依据，只有这样才能确保生效。而新人在自定义路由规则时常犯的一个错误就是：**在一条规则内同时匹配了多种不同的匹配依据，造成匹配无效。**

比如，他希望实现的配置是：

1. 自己的 `direct.yourdomain.com` 直连
2. 国内 DNS 查询（如 `223.5.5.5`）直连

### 7.1 错误示范

为了实现上面的目标，他写出了以下路由规则：

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "ip": ["223.5.5.5"],
        "domain": ["full:direct.yourdomain.com"],
        "outboundTag": "direct-out"
      }
    ]
  }
}
```

你能看出这里面的错误吗？乍一看，似乎是对的？

::: warning 注意
**同一个规则之内，各个依据需要同时成立，才会匹配成功**，逻辑关系是 `和`，而不是 `或`。
:::

换言之，这条规则的意思是：【当你访问的 `目标 = direct.yourdomain.com`, **并且** 同时还满足 `目标 = 223.5.5.5` 时，`Xray` 才会将流量转发给 `[direct-out]` 直连出站】

很显然，一个目标不可能同时等于两个不同的值，所以这不但是一个永远不可能实现的无效规则，更与原本的目标风马牛不相及。

### 7.2 正确示范

正确示范，自然就是将不同的匹配依据独立出来：

```json
{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "ip": ["223.5.5.5"],
        "outboundTag": "direct-out"
      },
      {
        "domain": ["full:direct.yourdomain.com"],
        "outboundTag": "direct-out"
      }
    ]
  }
}
```

其实，第 6 点已经是我整理过的规则了，原则就是【相同的匹配依据可以合并，不同的匹配依据保持独立】。

## 8. 明修栈道、暗渡陈仓

> `[domain]` 转化 `[ip]` 的密道：`domainStrategy`

我们在 5.4 中提交了多种流量判断的【依据】，其中一种是域名 `[domain]`、一种是 `[IP]`。

如果你初步了解过 DNS 的运作过程，就会知道，我们对一个域名 `[domain]` 发起访问请求时，其实需要先向 `DNS` 发起请求来解析域名 `[domain]` 对应的 `[IP]`，在得到 `[IP]` 后再向它发起实际请求。

所以，面对入站的一次域名请求，`Xray` 其实有两次机会去判断它的类型。那么，究竟是否要用这两次机会呢？这就是由 `domainStrategy` 这个配置来决定的。它有三个选项：

* `AsIs`
* `IPIfNonMatch`
* `IPOnDemand`

按么我们逐个来解释一下：

### 8.1 域名策略: `"AsIs"`

就是 "As Domain Is"，也就是说 【域名什么样，就什么样，不多折腾】。

简单粗暴理解就是说【仅用 `[domain]` 来匹配】。

::: tip
`AsIs` 的实际意义为 【如原先所示，不加修改】，🍉 老师这里描述的不是很恰当。
:::

这个方式的处理都在 `Xray` 内部完成，没有与外界的数据往来，所以速度最快。它的兜底策略也很清晰：即前面所说的、无法匹配的域名自动转入第一条出站处理。所以，对于常规使用路由功能这最推荐的策略。

### 8.2 域名策略: `"IPIfNonMatch"`

就是 "lookup IP if (there's) no matching rule"，也就是说【如果其他所有规则都匹配不上，那就转化成 `IP` 去匹配 `IP` 规则】。

简单粗暴理解就是说【先把访问目标和其他所有类型规则匹配，如果匹配不上，那就通过 `DNS` 查询转化成 `IP`，再从头和所有规则匹配一次】。

该策略下没有命中任何规则的这一部分域名，会需要再经历 `DNS` 查询过程、以及第二轮规则匹配的过程，其耗时会多于 `AsIs` 策略，所以并不是首选推荐的策略。

### 8.3 域名策略: `"IPOnDemand"`

这里其实说 `Demand IP` 更准确些，也就是说【当匹配时碰到任何基于 IP 的规则，将域名立即解析为 IP 进行匹配】。

简单粗暴理解就是说【只要路由规则中有 `IP` 类规则，那么所有基于域名 `[domain]` 的请求都要解析成 `[IP]` 然后去匹配 `[IP]` 类规则】。

它要对所有首次域名访问进行 `DNS` 解析，所以首次查询比较耗时。虽然由于 `Xray` 中 `DNS` 缓存机制的存在，后续对相同域名的访问速度会重回巅峰，但总体来说也不是首选推荐的策略。

::: warning 啰嗦君
`domainStrategy` 仅对域名生效，不要搞混了哦~
:::

## 9. 思考题

迄今为止，我们都是在【单入站】和【单出站】的基础上，讲解【路由】内部的各种配置逻辑。

但是，如你所知，`Xray` 本身是支持多端口，多协议的。那么，如果我问你：

1. 我希望 `VLESS` 协议将我日常的网页浏览和 APP 流量转发给美国的大流量服务器
2. 我希望 `trojan` 协议将我的所有 Netflix 流量转发给日本的服务器解锁各种二次元
3. 我希望 `shadowsocks` 协议将我所有的游戏流量转发给香港的服务器达到最低的延迟
4. 我希望有一个独立的端口，能够把 `telegram` 的流量全都转发给 VPS
5. 我希望有一个独立的端口，能够把 `bittorrent` 下载流量全都转发给欧洲大盘鸡
6. 我希望......

这些想法，是否能通过【路由】功能配置实现呢？

答案当然是 **【完全可以】** 啦！ 但是这些对于 `level-1` 来说已经超纲了，就留给各位自由的探索吧！

## 10. 结语

至此，`Xray` 的【路由】功能就介绍完了。希望本文能够对你理解 `Xray` 的灵活有所帮助。

## 11. 尾注

* 现在你可以重新阅读一遍 [路由](../../config/routing.md)，看看是否有更加深刻的理解。
* 🍉🍉🍉🍉🍉 :D

---

---
url: /document/level-1/work.md
---
# Xray 的工作模式

## 单服务器模式

与其它的网络代理工具一样，你需要一台配置了 Xray 的服务器，然后在自己的设备上安装并配置 Xray 客户端，然后即可流畅地访问互联网。

```mermaid
graph LR;
A(PC) -.- B(防火墙);
B -.-> C(墙外网站);
A --> D(Xray/VPS);
D --> C;
A --> E(墙内网站);
```

一个 Xray 服务器可同时支持多台设备使用不同的代理协议访问。同时，经过合理的配置，Xray 可以识别并区分需要代理以及不需要代理的流量，直连的流量不需要绕路。

## 桥接模式

如果你不想在每一台设备上都配置路由，你也可以设置一台中转服务器，用于接收客户端发来的所有流量，然后在服务器中进行转发判断。

```mermaid
graph LR;
A(PC) -.-> B(防火墙);
B -.-> C(墙外网站);
A --> D(墙内 VPS);
D --> E(墙外 VPS);
E --> C;
D --> F(墙内网站);
```

## 工作原理

在配置 Xray 之前，不妨先来看一下 Xray 的工作原理，以下是单个 Xray 进程的内部结构示意图。多个 Xray 之间相互独立，互不影响。

```mermaid
graph LR;
A1(inbound) --> D(Dispatcher / Router / DNS);
A2(inbound) --> D;
A3(inbound) --> D;
A4(inbound) --> D;
D --> B1(outbound);
D --> B2(outbound);
D --> B3(outbound);
D --> B4(outbound);
```

* 需要配置至少一个入站连接（Inbound）和一个出站连接（Outbound）才可以正常工作。
  * 入站连接负责与客户端（如浏览器）通信：
    * 入站连接通常可以配置用户认证，如 ID 和密码等；
    * 入站连接收到数据之后，会交给分发器（Dispatcher）进行分发；
  * 出站连接负责将数据发给服务器，如另一台主机上的 Xray。
* 当有多个出站连接时，可以配置路由（Routing）来指定某一类流量由某一个出站连接发出。
  * 路由会在必要时查询 DNS 以获取更多信息来进行判断。

---

---
url: /document/level-1/fallbacks-with-sni.md
---
# 通过 SNI 回落功能实现伪装与按域名分流

VLESS 是一种很轻的协议，和 Trojan 一样，不对流量进行复杂的加密和混淆，而是大隐隐于市，通过 TLS 协议加密，混杂在其他 HTTPS 流量中，在墙内外穿进穿出。为了更好的伪装以应对主动探测，Fallbacks 回落功能随 VLESS 同时出现。这篇教程将演示如何使用 Xray 中 VLESS 入站协议的回落功能配合 Nginx 或 Caddy 在保证伪装完全的前提下实现按域名分流。

## 应用情景

由于 XTLS，Xray 需要监听 443 端口，这导致如果之前有网站运行在服务器上，那么此时网站无法运行或需要运行在其他端口上，这显然是不合理的。有以下三种方案可以解决这个问题：

* Xray 监听其他常用端口（如 22、3389、8443）

  这个方案是最简单的，但不够完美。

* Nginx 或 HAProxy 监听 443 端口，通过 SNI 分流做 L4 反向代理，实现端口复用

  这个方案比较复杂，需要对 Nginx 或 HAProxy 的使用有一定了解，此处不作过多解释。

* Xray 监听 443 端口，通过 Fallbacks 功能 SNI 分流将网站流量回落到 Nginx 或 Caddy

  这个方案难度适中，也是此教程接下来想要演示的方案。

## SNI 简介

服务器名称指示（英语：**S**erver **N**ame **I**ndication，缩写：**SNI**）是 TLS 的一个扩展协议。熟悉反向代理的朋友都知道，如果想要通过域名将流量代理到正确的内容上，需要以下配置：

```nginx
proxy_set_header Host 主机名;
```

这句的作用是将名为 “Host” 的 HTTP Header 设定为某个主机名。为什么要这样做？一般而言，一台服务器对应一个 IP，但却运行多个网站，访问者通过域名查询到 IP 以访问服务器，那么问题来了，如何确定访问者想要访问的是哪一个网站？这需要“基于名称的虚拟主机”。

当 Web 服务器收到访问请求后，它会查看请求的主机头，使访问者访问正确的网站。然而当 HTTP 协议被 TLS 协议加密后，这种简单的方法就无法实现了。因为 TLS 握手发生在服务器看到任何 HTTP 头之前，因此，服务器不可能使用 HTTP 主机头中的信息来决定呈现哪个证书，更无法决定访问者的访问目标。

SNI 的原理也很简单，它通过让客户端发送主机名作为 TLS 协商的一部分来解决此问题。所以在使用 Nginx 对 HTTPS 协议进行反向代理时，需要在配置中加入 `proxy_ssl_server_name on;`，此时 Nginx 会向被代理的服务器发送 SNI 信息，解决了 HTTPS 协议下虚拟主机失效的问题。另外，使用 SNI 时，即使不指定主机头，也可以正确访问网站。

## 思路

![Xray 回落流程](./fallbacks-with-sni-resources/xray-fallbacks.svg)

从 443 端口接收到流量后，Xray 会把 TLS 解密后首包长度 < 18、协议版本无效或身份认证失败的流量通过对 name、path、alpn 的匹配转发到 dest 指定的地址。

## 添加 DNS 记录

![DNS 记录](./fallbacks-with-sni-resources/xray-dns-records.webp)

请按实际情况修改域名和 IP。

## 申请 TLS 证书

由于要对不同前缀的域名进行分流，但一个通配符证书的作用域仅限于两“.”之间（例如：申请 `*.example.com`，`example.com` 和 `*.*.example.com` 并不能使用该证书），故需申请 [SAN](https://zh.wikipedia.org/wiki/%E4%B8%BB%E9%A2%98%E5%A4%87%E7%94%A8%E5%90%8D%E7%A7%B0) 通配符证书。根据 Let's Encrypt 官网信息\[^1]，申请通配符证书要求 DNS-01 验证方式，此处演示 NS 记录为 Cloudflare 的域名通过 [acme.sh](https://acme.sh) 申请 Let's Encrypt 的免费 TLS 证书。使用其他域名托管商的申请方法请阅读 [dnsapi · acmesh-official/acme.sh Wiki](https://github.com/acmesh-official/acme.sh/wiki/dnsapi)。

首先需要到 [Cloudflare 面板](https://dash.cloudflare.com/profile/api-tokens)创建 API Token。参数如下：

![API Token 的权限设置](./fallbacks-with-sni-resources/cf-api-token-permissions-for-acme.webp)

权限部分至关重要，其他部分任意。

创建完毕后，你会得到一串神秘字符，请将其妥善保管到安全且不会丢失的地方，因为它不再会显示。这串字符就是即将用到的 `CF_Token`。

::: tip 注意
以下操作需要在 root 用户下进行，使用 sudo 会出现错误。
:::

```bash
curl https://get.acme.sh | sh # 安装 acme.sh
export CF_Token="sdfsdfsdfljlbjkljlkjsdfoiwje" # 设定 API Token 变量
acme.sh --issue -d example.com -d *.example.com --dns dns_cf # 使用 DNS-01 验证方式申请证书
mkdir /etc/ssl/xray # 新建证书存放目录
acme.sh --install-cert -d example.com --fullchain-file /etc/ssl/xray/cert.pem --key-file /etc/ssl/xray/privkey.key --reloadcmd "chown nobody:nogroup -R /etc/ssl/xray && systemctl restart xray" # 安装证书到指定目录并设定自动续签生效指令
```

## Xray 配置

```json
{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "users": [
          {
            "id": "UUID",
            "flow": "xtls-rprx-vision"
          }
        ],
        "decryption": "none",
        "fallbacks": [
          {
            "name": "example.com",
            "path": "/vmessws",
            "dest": 5000,
            "xver": 1
          },
          {
            "dest": 5001,
            "xver": 1
          },
          {
            "alpn": "h2",
            "dest": 5002,
            "xver": 1
          },
          {
            "name": "blog.example.com",
            "dest": 5003,
            "xver": 1
          },
          {
            "name": "blog.example.com",
            "alpn": "h2",
            "dest": 5004,
            "xver": 1
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "alpn": ["h2", "http/1.1"],
          "certificates": [
            {
              "certificateFile": "/etc/ssl/xray/cert.pem",
              "keyFile": "/etc/ssl/xray/privkey.key"
            }
          ]
        }
      }
    },
    {
      "listen": "127.0.0.1",
      "port": 5000,
      "protocol": "vmess",
      "settings": {
        "users": [
          {
            "id": "UUID"
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
          "acceptProxyProtocol": true,
          "path": "/vmessws"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
```

以上配置针对于 Nginx，以下是需要注意的一些细节。

* 有关 Proxy Protocol

  Proxy Protocol 是 HaProxy 开发的一种旨在解决代理时容易丢失客户端信息问题的协议，常用于链式代理和反向代理。传统的处理方法往往较为复杂且有诸多限制，而 Proxy Protocol 非常简单地在传输数据时附带上原始连接四元组信息的数据包，解决了这个问题。

  凡事皆有利弊，Proxy Protocol 也是如此。

  * 有发送必须有接收，反之亦然
  * 同一端口不能既兼容带 Proxy Protocol 数据的连接又兼容不带数据的连接（如：Nginx 同端口的不同虚拟主机（server），本质是上一条）\[^2]\[^3]

  在遇到异常时，请考虑配置是否符合上述条件。

  此处，我们使用 Proxy Protocol 让被回落到的目标获取到客户端的真实 IP。

  另外，当 Xray 的某个入站配置存在 `"acceptProxyProtocol": true` 时，ReadV 将失效。

* 有关 HTTP/2

  首先，`inbounds.streamSettings.tlsSettings.alpn` 有顺序，应将 `h2` 放前，`http/1.1` 放后，在优先使用 HTTP/2 的同时保证兼容性；反过来会导致 HTTP/2 在协商时变为 HTTP/1.1，成为无效配置。

  在上述配置中，每条回落到 Nginx 的配置都要分成两个。这是因为 h2 是强制 TLS 加密的 HTTP/2 连接，这有益于数据在互联网中传输的安全，但在服务器内部没有必要；而 h2c 是非加密的 HTTP/2 连接，适合该环境。然而，Nginx 不能在同一端口上同时监听 HTTP/1.1 和 h2c，为了解决这个问题，需要在回落中指定 `alpn` 项（是 `fallbacks` 而不是 `tlsSettings` 里面的），以尝试匹配 TLS ALPN 协商结果。

  建议 `alpn` 项只按需用两种填法：\[^4]

  * 省略
  * `"h2"`

  如果使用 Caddy 就大可不必如此繁杂了，因为它**可以**在同一端口上同时监听 HTTP/1.1 和 h2c，配置改动如下：

  ```json
  {
    "fallbacks": [
      {
        "name": "example.com",
        "path": "/vmessws",
        "dest": 5000,
        "xver": 1
      },
      {
        "dest": 5001,
        "xver": 1
      },
      {
        "name": "blog.example.com",
        "dest": 5002,
        "xver": 1
      }
    ]
  }
  ```

## Nginx 配置

Nginx 将通过官方源进行安装。

```bash
sudo apt install curl gnupg2 ca-certificates lsb-release
echo "deb [arch=amd64] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt update
sudo apt install nginx
```

删除 `/etc/nginx/conf.d/default.conf` 并创建 `/etc/nginx/conf.d/fallbacks.conf`，内容如下：

```nginx
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;

server {
    listen 127.0.0.1:5001 proxy_protocol default_server;
    listen 127.0.0.1:5002 proxy_protocol default_server http2;

    location / {
        root /srv/http/default;
    }
}

server {
    listen 127.0.0.1:5003 proxy_protocol;
    listen 127.0.0.1:5004 proxy_protocol http2;

    server_name blog.example.com;

    location / {
        root /srv/http/blog.example.com;
    }
}

server {
    listen 80;
    return 301 https://$host$request_uri;
}
```

## Caddy 配置

安装 Caddy 请参阅 [Install — Caddy Documentation](https://caddyserver.com/docs/install)。

为了使 Caddy 能获取到访问者的真实 IP，需要编译带有 Proxy Protocol 模块的 Caddy。建议直接在 Caddy 官网上在线编译。

```bash
sudo curl -o /usr/bin/caddy "https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fmastercactapus%2Fcaddy2-proxyprotocol&idempotency=79074247675458"
sudo chmod +x /usr/bin/caddy
```

直接替换即可。

::: tip
建议先通过官网文档安装 Caddy，再替换二进制文件。这样做无需手动设定进程守护。
:::

编辑 `/etc/caddy/Caddyfile`：

```Caddyfile
{
    servers 127.0.0.1:5001 {
        listener_wrappers {
            proxy_protocol
        }
	protocol {
            allow_h2c
        }
    }
    servers 127.0.0.1:5002 {
        listener_wrappers {
            proxy_protocol
        }
	protocol {
            allow_h2c
        }
    }
}

:5001 {
    root * /srv/http/default
    file_server
    log
    bind 127.0.0.1
}

http://blog.example.com:5002 {
    root * /srv/http/blog.example.com
    file_server
    log
    bind 127.0.0.1
}

:80 {
    redir https://{host}{uri} permanent
}
```

## 参考

1. [服务器名称指示 - 维基百科，自由的百科全书](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%90%8D%E7%A7%B0%E6%8C%87%E7%A4%BA)
2. [Home · acmesh-official/acme.sh Wiki](https://github.com/acmesh-official/acme.sh/wiki)
3. [HTTP/2 - 维基百科，自由的百科全书](https://zh.wikipedia.org/wiki/HTTP/2)

## 引用

\[^1]: [常见问题 - Let's Encrypt - 免费的 SSL/TLS 证书](https://letsencrypt.org/zh-cn/docs/faq/)
\[^2]: [Proxy Protocol - HAProxy Technologies](https://www.haproxy.com/blog/haproxy/proxy-protocol/)
\[^3]: [proxy protocol 介绍及 nginx 配置 - 简书](https://www.jianshu.com/p/cc8d592582c9)
\[^4]: [v2fly-github-io/vless.md at master · rprx/v2fly-github-io](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md)

---

---
url: /document/level-1/routing-with-dns.md
---
# 用 DNS 实现精准境内外分流

## 常规分流方式以及缺陷

当你尝试手搓代理规则时，必定会有这样的疑问：我该让哪些流量走代理，哪些直连？

答案一般就是黑/白名单。

十几年下来，社区维护了一个庞大的规则列表，先后诞生了许多优秀项目：

* https://github.com/gfwlist/gfwlist
* https://github.com/v2fly/domain-list-community
* https://github.com/Loyalsoldier/v2ray-rules-dat

但它们不可能穷尽所有的网站，具有滞后性，而且做不到 100% 可信。

随便举几个例子：

* geosite:cn 是一个大杂烩，只要和中国有一点关系的都往里装。甚至域名被墙了也没能及时移除。
  如果你仅凭目标域名在其中就走直连是行不通的，比如 ai.ytimg.com、login.corp.google.com 就被墙了迟迟没有移除。
* v2ray-rules-dat 的 [README](https://github.com/Loyalsoldier/v2ray-rules-dat) 写到：Apple、微软、Google CN 的域名同时存在于 geosite:cn 和 geosite:geolocation-!cn 中，但实际上不是这样。(See: [PR#328](https://github.com/Loyalsoldier/v2ray-rules-dat/pull/328))
* 如果域名不在名单里呢？

这无疑给分流带来困扰，如果你的规则更新的不及时，你可能会出现本应走直连的流量被代理、甚至一些网站会打不开的现象。

未知的域名假如在中国有服务器，希望尽可能直连，但一顿操作后遇到了传说中的 [DNS Leak](https://github.com/XTLS/BBS/issues/3#issuecomment-3505661189) 怎么办？

那么有没有什么办法能做到 99.99% 安全又精准的分流呢？

答案是：包的。

## 利用 Xray-core DNS 模块实现精准分流

合理利用 Xray ~~如轮椅般~~强大的内置 DNS 自带的回落、ECS、IP 过滤、打 Tag 等功能，精心调整它们的顺序。如此你便得到了比 geosite cn/!cn 更为精确且实时的 IP 作为分流条件，因为 IP 归属地，特别是 cn 归属地变更频率较低。

在继续阅读本文之前，你需要充分阅读并理解“入门技巧：路由 (routing) 功能简析[上篇](./routing-lv1-part1.md)、[下篇](./routing-lv1-part2.md)”。
与此同时你已经快要把官方配置指南给翻烂了，因此你完全理解了路由和出站中的 domainStrategy、入站中 sniffing 各选项的作用、以及其不同值的组合下产生的行为。

一切就绪？请试着理解下面这段内容：

socks、http 入站时，请求的就是域名，到了路由后，路由中非 AsIs 的 domainStrategy 可以利用内置 DNS 解析出 IP 临时用于路由匹配。到了本地 direct 出站时，出站中非 AsIs 的 domainStrategy 可以利用内置 DNS 再次解析出 IP 用于出站。发往 Xray 服务器的请求只有域名，具体访问哪个 IP 取决于服务器的 direct 出站。

透明代理时情况变得更加复杂，入站 sniffing 开启，且 destOverride 有 \[http, tls]：

* 若 routeOnly = false 则请求的 IP 将被抹掉，后面的流程跟 socks 入站一样。
* 若 routeOnly = true 则同时有域名和 IP，到了路由后，可以直接匹配域名和 IP 规则，本地 direct 出站也会用此 IP。发往 Xray 服务器的请求只有 IP，服务器如何处理？再把刚才的流程走一遍。

遇到困难？你需要继续反复阅读官方指南并尝试理解。否则你很难利用到下面示例中 DNS 模块的解析结果来正确分流。

***

#### 例子 1：此配置解析出精准的 CDN 友好的 IP 地址，保证无 DNS Leak 的同时，只要中国有服务器节点就能优先解析出来，非常适合 realIp 透明代理场景。

```json
{
  "dns": {
    "servers": [
      // 防止谷歌人机验证卡住、防止谷歌中国被监控（因为大量三方网站可能加载谷歌字体之类的）
      {
        "address": "1.1.1.1",
        "skipFallback": true,
        "domains": ["geosite:google", "geosite:google-cn"]
      },
      {
        "address": "8.8.8.8",
        "skipFallback": true,
        "domains": ["geosite:google", "geosite:google-cn"],
        "finalQuery": true // 终结查询链
      },
      // 通过直连解析社区认为在中国有节点的域，若非预期结果可能是被墙或退出中国
      // 通过代理解析社区认为在中国有节点的域，供被墙或退出中国时回落
      {
        "tag": "dns-direct",
        "address": "114.114.114.114",
        "skipFallback": true,
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      {
        "tag": "dns-direct",
        "address": "223.5.5.5",
        "skipFallback": true,
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      {
        "address": "1.1.1.1",
        "skipFallback": true,
        "domains": ["geosite:cn"]
      },
      {
        "address": "8.8.8.8",
        "skipFallback": true,
        "domains": ["geosite:cn"],
        "finalQuery": true // 终结查询链
      },
      // 通过代理解析社区认为非中国本土的域，若非预期结果则尝试优化直连
      {
        "address": "1.1.1.1",
        "skipFallback": true,
        "domains": ["geosite:geolocation-!cn"],
        "expectIPs": ["!geoip:cn"]
      },
      {
        "address": "8.8.8.8",
        "skipFallback": true,
        "domains": ["geosite:geolocation-!cn"],
        "expectIPs": ["!geoip:cn"]
      },
      {
        "address": "8.8.8.8",
        "clientIp": "222.85.85.85", // 提供你当地ISP的IP地址以获取直连优化的A/AAAA记录
        // 比如你是河南电信，就可以使用荷兰电信DNS
        // 不能保证100%中国CDN友好，因为不是所有权威都支持ECS
        "skipFallback": true,
        "domains": ["geosite:geolocation-!cn"]
      },
      {
        "address": "8.8.4.4",
        "clientIp": "222.85.85.85", // 同上
        "skipFallback": true,
        "domains": ["geosite:geolocation-!cn"],
        "finalQuery": true // 终结查询链
        // 看到这里你可能会有疑惑：这4条规则和上面4条感觉有点冗余？可以精简吗？
        // 其实不然，这是为了追求更极致的速度，以及一些权威DNS不支持ECS
      },
      // 未知域名，中国优先，若非预期结果则尝试优化代理
      {
        "address": "8.8.8.8",
        "clientIp": "222.85.85.85", // 同上
        "expectIPs": ["geoip:cn"]
      },
      {
        "address": "8.8.4.4",
        "clientIp": "222.85.85.85", // 同上
        "expectIPs": ["geoip:cn"]
      },
      "1.1.1.1",
      "8.8.8.8"
    ],
    "tag": "dns-proxy",
    "enableParallelQuery": true // 智能并行：全部并行，智能分组，组内竞速
  },
  "routing": {
    "domainStrategy": "具体取决于你的需求",
    "rules": [
      {
        // 为 DNS 查询本身进行路由
        "inboundTag": ["dns-direct"],
        "outboundTag": "direct"
      },
      {
        // 为 DNS 查询本身进行路由
        "inboundTag": ["dns-proxy"],
        "outboundTag": "proxy"
      }
      // 你的个性化分流规则
      // 对于流媒体解锁等你应用 domain 分流，对于境内外分流你始终应用 ip 来分流
    ]
  }
  // 其它忽略，按需配置...
}
```

```mermaid
graph TD
    A[收到 DNS 查询请求] --> B{域名分类判断};

    B -->|geosite:google<br>谷歌域名| C["通过<b>代理</b>查询<br>国外DNS (1.1.1.1...)"];
    C --> C_OUT[大概率返回国外IP];
    C_OUT --> Z[结束查询];

    B -->|geosite:cn<br>已知疑似国内域名| D["通过<b>直连</b>查询<br>国内DNS (114, AliDNS)"];
    D --> E{解析结果是国内IP?};
    E -->|"是 (符合预期)"| F_OUT[返回国内IP];
    F_OUT --> Z;
    E -->|"否 (被污染/分类错/退华)"| G["<b>回落</b>: 通过<b>代理</b>查询<br>国外DNS (1.1.1.1...)"];
    G --> G_OUT[返回正确的国外IP];
    G_OUT --> Z;

    B -->|geosite:geolocation-!cn<br>已知疑似国外域名| I["通过<b>代理</b>查询<br>国外DNS (1.1.1.1...)"];
    I --> J{解析结果是国外IP?};
    J -->|"是 (符合预期)"| K_OUT[返回国外IP];
    K_OUT --> Z;
    J -->|"否 (意外返回国内IP 分类错)"| L["<b>回落</b>: 通过<b>代理+ECS</b>查询<br>国外DNS (8.8.8.8...)"];
    L --> L_OUT[返回优化的国内IP];
    L_OUT --> Z;

    B -->|未知域名| N["通过<b>代理+ECS</b>查询<br>国外DNS (8.8.8.8...)<br>期望获得国内IP"];
    N --> O{解析结果是国内IP?};
    O -->|"是 (有国内服务器)"| P_OUT[返回国内IP];
    P_OUT --> Z;
    O -->|"否 (纯国外网站)"| Q["<b>回落</b>: 通过<b>代理</b>查询<br>国外DNS (1.1.1.1...)"];
    Q --> Q_OUT[返回优化的国外IP];
    Q_OUT --> Z;

```

你可以根据此配置解析出的 IP 结合域名、或者完全靠 IP 来分流。

realIp 透明代理环境，你甚至可以在保证完全劫持各种渠道的 DNS 后，设置 domainStrategy=AsIs、routeOnly=true 做全程无二次 DNS 解析。

> 注意：上面说的 CDN 友好，境外部分是针对你代理服务器所在位置做的优化，如果你是仅黑名单走代理，而非全部境外流量都走代理，需要自行调整规则中的 ECS。

#### 例子 2：此配置解析出正确，但不保证国外 CDN 友好的地址，保证无 DNS Leak 的同时，只要中国有服务器节点就能优先解析出来，适合 fakeIp 透明代理、socks、http 入站等场景。

```json
{
  "dns": {
    "servers": [
      // 防止谷歌人机验证卡住、防止谷歌中国被监控（因为大量三方网站可能加载谷歌字体之类的）
      {
        "address": "1.1.1.1",
        "skipFallback": true,
        "domains": ["geosite:google", "geosite:google-cn"],
        "finalQuery": true // 终结查询链
      },
      {
        // 我们不完全信任geosite:cn，但如果一个域名已经存在于此列表
        // 就优先试着解析一下，如果返回中国IP代表它没被墙
        // 反之高度疑似被墙，回落到8.8.8.8重新解析，解决可能的DNS污染
        // 优先让它解析的原因是代价极小，走直连只需要十几毫秒
        "tag": "dns-direct",
        "address": "223.5.5.5",
        "skipFallback": true,
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      {
        // 如果一个域名不存在于geosite:cn，或者是上面的规则回落下来的，就会使用此服务器
        // 这里利用ECS尝试获取中国的A/AAAA记录
        "address": "8.8.8.8",
        "clientIp": "222.85.85.85", // 提供当地ISP的IP地址以获取直连优化的A/AAAA记录
        // 比如你是河南电信，就可以使用荷兰电信DNS
        // 不能保证100%中国CDN友好，因为不是所有权威都支持ECS
        "skipFallback": false
      }
    ],
    "tag": "dns-proxy"
  },
  "routing": {
    "domainStrategy": "必须是非 AsIs，具体取决于你的需求",
    "rules": [
      {
        // 为 DNS 查询本身进行路由
        "inboundTag": ["dns-direct"],
        "outboundTag": "direct"
      },
      {
        // 为 DNS 查询本身进行路由
        "inboundTag": ["dns-proxy"],
        "outboundTag": "proxy"
      }
      // 你的个性化分流规则
      // 对于流媒体解锁等你应用 domain 分流，对于境内外分流你始终应用 ip 来分流
    ]
  }
  // 其它忽略，按需配置...
}
```

此场景下由于发给 Xray 服务器的请求全部都是域名，因此没有必要利用 DNS 反复试探最优结果，只需要快速识别域是否被污染，尽可能解析出中国的 CDN 友好的 IP 即可。

此示例中 DNS 模块解析出的中国 IP 已经是 99% 中国 CDN 友好的，因此你可以在 direct 出站中 `domainStrategy` 设为**非** AsIs 以利用缓存，如果你需要的话；
如果你追求 100% 的中国 CDN 友好，可设为 AsIs 利用操作系统设置的 DNS 再解析一次，额外耗时约 1 ~ 数百毫秒，建议开启乐观缓存以进一步降低延迟。

## 写在后面

已知许多国内无良 App 会探测你的海外出口 IP，并将其和你 GPS 定位、手机号、外卖地址等敏感信息关联起来，并泄露给社工库。~~Big brother is watching u!!!~~

总有人误解这是因为分流导致的，只要改用黑名单模式（仅黑名单网站走代理）就能规避。
实则不然，首先黑名单非常容易投毒，他们可以故意制造诱饵网站提交进去来探测你的海外 IP。

其次只要黑名单中任何一个网站托管在 Cloudflare 甚至都不需要诱饵，试着访问下：https://chatgpt.com/cdn-cgi/trace

聪明的你可能会想到：那我再找一个公共代理套一层不就解决了吗？前提是你要完全信任此代理不留日志不出卖你，并且它被许多你国家的人高强度使用。这样一个海外 IP 在单位时间内与成千上万人产生关联，就没有办法通过海外 IP 倒推出你了。至于这种脏 IP 的烦人验证码问题克服一下就行。

赛博大善人 Cloudflare 的 Warp 不正好完美契合所有特征吗？可惜的是对于托管在 CF 的网站，Warp 并不能完全隐藏你的 IP，它仅对非 CF 服务器有效。

Tor 可以吗？它不太适合日常使用，IP 太脏且出口频繁跳跃许多网站会因此封停你的账号。

因此除非你使用的客户端能按 App 分流（这仅能在手机、电脑上实现）其它任何爬墙方式都会导致你的海外 IP 泄露。

总之我想说的是对于常规爬墙方式，海外 IP 泄露几乎无法避免，如果你需要高级隐私保护，请使用 Tor 等工具。Xray-core 作为抗审查工具侧重于抗阻断帮你穿越防火墙，而在隐私保护的方面能力十分有限。

---

---
url: /document/level-2.md
---
# 进阶文档

**这个章节包含进阶级的 Xray 使用心得分享, 如果您已经熟悉 Xray, 那么这里的经验可以让您更加发挥 Xray 的威力**

[透明代理入门](./transparent_proxy/transparent_proxy.md) by  [@kirin](https://github.com/kirin10000)

透明代理的入门篇章。

***

[透明代理（TProxy）配置教程 ](./tproxy.md) by  [@BioniCosmos](https://github.com/BioniCosmos)

基于 Xray 的透明代理（TProxy）配置完整教程。

***

[TProxy 透明代理（ipv4 and ipv6）配置教程](./tproxy_ipv4_and_ipv6.md) by  [@SQLimit](https://github.com/SQLimit)

基于 Xray 的 TProxy 透明代理（ipv4 and ipv6）配置教程

***

[Nginx 或 Haproxy 搭建 TLS 隧道隐藏指纹](./nginx_or_haproxy_tls_tunnel.md) by  [@SQLimit](https://github.com/SQLimit)

双端使用 Nginx 或 Haproxy 搭建 TLS 隧道隐藏指纹

***

[\[透明代理\]通过 gid 规避 Xray 流量](./iptables_gid.md) by  [@kirin](https://github.com/kirin10000)

在 iptables/nftables 实现的透明代理中，一种新的规避 Xray 流量的方式。

***

[通过 Xray 将特定的流量指向特定出口，实现全局路由“分流”](./redirect.md) by  [@Zzz3m](https://github.com/Zzz3m)

将 Xray 玩出花:基于 fwmark 、 sendThrough 或 sockopt.interface 方式实现“分流”。

***

[通过 Cloudflare Warp 增强代理安全性](./warp.md) by  [@yuhan6665](https://github.com/yuhan6665)

Xray v1.6.5 新增 WireGuard 出站的使用介绍。

***

[Xray 流量统计](./traffic_stats.md) by  [@yuhan6665](https://github.com/yuhan6665)

适配 Xray 的流量统计和脚本。

***

[VLESS 反向代理](./vless_reverse.md)

VLESS 反向代理教程。

---

---
url: /document/level-2/transparent_proxy/transparent_proxy.md
---
# 透明代理入门

## 什么是透明代理

透明代理简单地说就是不让被代理的设备感觉到自己被代理了。简单地说就是，被代理的设备上不需要运行任何代理软件(比如 Xray、V2RayNG 等)，当你连接上网络时，你的设备已经被代理了。

这也意味着，代理的软件运行在别的地方，比如运行在路由器中，通过路由器上网的设备就自动被代理了。

## 透明代理的实现

透明代理的实现目前主要有两种方式：

### tun2socks

可用 Windows/Linux(包括安卓)实现。因为实现过程比较简单，很少有教程，我这里简单描述一下。

**Windows**

1. 安装 **[Netch](https://github.com/NetchX/Netch/releases)** ，使用模式`[3] [TUN/TAP] 绕过局域网`启动。

2. 开启热点

3. 打开`控制面板`->`网络和 Internet`->`网络和共享中心`->`更改适配器设置`，找到`TAP-Windows Adapter`和`Microsoft Wi-Fi Direct Virtual Adapter`。

4. 鼠标右键点击`TAP-Windows Adapter`，`属性`->`共享`，勾选`允许其他网络用户通过此计算机的 Internet 连接来连接`，在`家庭网络连接`中选择`Microsoft Wi-Fi Direct Virtual Adapter`的那个网络连接，点击确定。

**Android**

1. 配置连接 V2RayNG

2. 开启热点

3. 热点设置 -> 允许热点使用 VPN(部分安卓系统可能没有这个选项)

### iptables/nftables

iptables 与 nftables 实现透明代理的原理相同，下文统一使用 iptables。

基于 iptables 的透明代理实现只能用于 Linux 系统(包括 openwrt/安卓)。由于其比 tun2socks 更高效率以及适合在路由器中配置而广泛使用。

现存的三篇白话文透明代理教程其实讲的都是基于这种方案的透明代理实现，它们是： **[新 V2Ray 白话文指南-透明代理](https://guide.v2fly.org/app/transparent_proxy.html)** 、 **[新 V2Ray 白话文指南-透明代理(TPROXY)](https://guide.v2fly.org/app/tproxy.html)** 、 **[透明代理（TProxy）配置教程](../tproxy.md)** 。其中第一篇是基于 iptables-redirect 模式，已经过时了，不建议使用，仅供参考。第二篇和第三篇讲的都是基于 iptables-tproxy 模式的透明代理实现。

## iptables 实现透明代理原理

Linux 使用`Netfilter`来管理网络，`Netfilter`模型如下：

![Netfilter](./netfilter.png)

**假设使用路由器作为网关(即我们平时的上网方式)，那么：**

局域网设备通过路由器访问互联网的流量方向：

`PREROUTING链->FORWARD链->POSTINGROUTING链`

局域网设备访问路由器的流量(如登陆路由器 web 管理界面/ssh 连接路由器/访问路由器的 dns 服务器等)方向：

`PREROUTING链->INPUT链->网关本机`

路由器访问互联网的流量方向：

`网关本机->OUTPUT链->POSTINGROUTING链`

**通过使用 iptables 操控`PREROUTING链`和`OUTPUT链`的流量走向，转发到 Xray，就可以代理局域网设备和网关本机。**

## 透明代理难在哪里

透明代理的难点就在于路由，所谓路由，就是区分哪些流量是直连的，哪些该被代理，所以我个人认为叫做**分流**更加合适。

我们可以把路由由易到难分为以下几个阶段：

1. 代理全部请求

2. 本地局域网 IP/组播 IP 请求直连，其它请求代理

3. 在 2 的基础上直连 Xray 发起的连接请求

4. 在 3 的基础上直连指向中国大陆 IP 的连接请求，并对国内外域名选择国内外 DNS 服务器解析。

上面说的三篇教程，都是在第四阶段。所以新手直接阅读可能显得有点难懂。

## 从零开始一步步实现基于 iptables-tproxy 的透明代理

### 在开始之前，你需要有一定的基础知识：

1. 大概知道什么是 TCP/IP 协议、域名和 DNS 服务器

2. 知道什么是 WAN 口，LAN 口，LAN\_IP，WAN\_IP 以及 DHCP 服务器。对于旁路由，只有一个网口，这里称其为 LAN 口

3. 对 Linux 系统有最基础的了解(知道怎么运行命令)

4. 能够手写客户端 json 文件配置，至少要能看懂

### 前期准备工作

::: warning
在开始操作前，记得使用 `sysctl -w net.ipv4.ip_forward=1` 打开linux ipv4封包转发
:::
**1. 准备一个运行 Linux 系统的网关**

比如，刷了 OpenWRT 的路由器

**2. 在网关(路由器)准备好 Xray 可执行文件以及配置文件**

配置文件监听 12345 端口，开启 tproxy：

```json
{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "allowedNetwork": "tcp,udp",
        "followRedirect": true
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy"
        }
      }
    }
  ],
  "outbounds": [
    {
      你的服务器配置
    }
  ]
}
```

我们由易到难，不写 routing，只写一个 inbound 一个 outbound。

### 首先，我们先试试做到第一阶段

::: warning
如果你不能接受你的机器需要重启，最好先开一个虚拟机进行练习
:::

将所有`PREROUTING链`的流量，都转发到 Xray 中。

运行 Xray，执行以下指令：

```bash
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N XRAY
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY
```

当你输入完之后，如果你是使用 ssh 连接到网关上的，你会发现 ssh 的连接断开了(不用紧张，断电重启即可恢复)，并且透明代理无法上网；如果你是的网关是虚拟机，你会发现网关本身也无法上网，并且 Xray 日志 access\_log 中出现许多源地址为目标地址，目标地址为 WAN\_IP 的请求。

理论上网关本机访问公网只会经过`OUTPUT链`和`POSTROUTING链`，为什么操控`PREROUTING链`会导致网关无法上网呢？这是因为网络通讯往往是双向的，虽然网关访问公网 IP 不需要经过`PREROUTING链`，但被访问的服务器向网关返回信息时要经过`PREROUTING链`，且这部分被转发到 Xray 了，因此出现了日志中的反向请求。

我们修改一下规则，源 IP 不是来自局域网的则返回。重启网关，运行 Xray，执行以下指令：

```bash
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N XRAY
# "网关LAN_IP地址段" 通过运行命令"ip address | grep -w "inet" | awk '{print $2}'"获得，是其中的一个
iptables -t mangle -A XRAY ! -s 网关LAN_IP地址段 -j RETURN
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY
```

然后你会发现，虽然 ssh 连接断开了，但是透明代理已经可用了。只要我们修改系统 dns 为公共 dns，就能正常上网了(因为现在网关访问不了，所以 dns 设置为网关是不行的)。

至此，第一阶段就完成了。之所以无法访问网关，是因为代理规则代理了全部流量，包括访问网关的流量。试想在 VPS 上访问你本地的网关，肯定是访问不了的，所以我们要对这部分流量直连，请看第二阶段：

### 第二阶段

重启网关，运行 Xray，执行以下指令：

```bash
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N XRAY

# 所有目标地址在网关所在网段的请求直连
# 通过运行命令"ip address | grep -w "inet" | awk '{print $2}'"获得，一般来说有多个
iptables -t mangle -A XRAY -d 网关所在网段1 -j RETURN
iptables -t mangle -A XRAY -d 网关所在网段2 -j RETURN
...

# 目标地址为组播IP/E类地址/广播IP的请求直连
iptables -t mangle -A XRAY -d 224.0.0.0/3 -j RETURN

iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY
```

使用这条规则后，上一条规则`iptables -t mangle -A XRAY ! -s 网关LAN_IP地址段 -j RETURN`便成为了多余规则，可以删去。

至此，第二阶段完成。网关已经可以访问，ssh 不会断开。

### 第三阶段

我们平时用的 DNS 一般来自路由器，但这个 iptables 规则只代理了局域网中的设备，却没有代理网关本机，这样返回的 DNS 查询结果可能是错误的或者污染的。

iptables-tproxy 不支持对`OUTPUT链`操作，但是我们可以通过配置`策略路由`，把`OUTPUT链`中相应的包重新路由到`PREROUTING链`上。

```bash
# 添加策略路由: 标记为1的包，走路由表100
ip rule add fwmark 1 table 100
# 添加路由条目到路由表100: 所有包路由到本地
ip route add local 0.0.0.0/0 dev lo table 100
```

通过配置上述`策略路由`，我们只需要在`OUTPUT链`中给包打标记为`1`，相应的包就会路由到网关本机，即`PREROUTING链`上。所以我们就给网关本机需要代理的请求在`OUTPUT链`上标记`1`即可。

如果要代理网关本机发出的的全部请求，就会引入一个问题，Xray 运行在网关，Xray 向代理服务端发送请求，这个请求又被代理了，就形成了回环。

因此要代理网关本机，就要避免回环发生，即代理规则中规避 Xray 请求的流量。

**常见的方法有三种：**

1. 直连目标地址为 VPS 的流量

重启网关，运行 Xray，执行以下指令：

```bash
#代理局域网设备
#继承上一个阶段的成果
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N XRAY
iptables -t mangle -A XRAY -d 网关所在网段1 -j RETURN
iptables -t mangle -A XRAY -d 网关所在网段2 -j RETURN
...
iptables -t mangle -A XRAY -d 224.0.0.0/3 -j RETURN
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY

#代理网关本机
iptables -t mangle -N XRAY_MASK
iptables -t mangle -A XRAY_MASK -d 网关所在网段1 -j RETURN
iptables -t mangle -A XRAY_MASK -d 网关所在网段2 -j RETURN
...
iptables -t mangle -A XRAY_MASK -d 224.0.0.0/3 -j RETURN
iptables -t mangle -A XRAY_MASK -d VPS公网ip/32 -j RETURN
iptables -t mangle -A XRAY_MASK -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -p tcp -j XRAY_MASK
iptables -t mangle -A OUTPUT -p udp -j XRAY_MASK
```

但是这么配置有个缺点，如果使用 CDN 或者 VPS 很多的话，就不好写规则了。

2. 通过 mark 规避

三个白话文教程都是使用这种方法规避，自行参考，这里不再赘述。

3. 通过 gid 规避(推荐)

参考 **[\[透明代理\]通过 gid 规避 Xray 流量](../iptables_gid.md)**

这样就完成了第三阶段的代理，也就是平时说的全局代理。但是记得把网关的 DNS 服务器设置为国外的 DNS 服务器，否则可能依然返回被污染的结果。

### 第四阶段

其实，并不是所有人都需要实现第四阶段。全局代理对于大部分情况已经适用。

特别是对于旁路由而言。需要代理时，将网关调成旁路由的 IP，不需要代理时，将网关换回主路由 IP。

至于第四阶段的具体实现，那三篇白话文教程讲的都是。在理解了上面的内容后，再去看那三篇白话文教程，就比较容易理解了。

### 代理 ipv6

上面的规则只对 ipv4 生效，如果还想要代理 ipv6 请求，则使用 ip6tables 命令，用法与 iptables 基本相同。参考 **[\[透明代理\]通过 gid 规避 Xray 流量#4-设置 iptables 规则](../iptables_gid#4-设置iptables规则.md)**

# iptables 透明代理的其它注意事项

1. 如果作为代理的网关作为主路由，要在`PREROUTING链`规则中加一条`iptables -t mangle -A XRAY ! -s 网关LAN_IP地址段 -j RETURN`，即在第一阶段使用、第二阶段被删除的指令。如果不写，WAN 口中同网段的其它人可以将网关填写成你的 WAN\_IP，从而蹭你的透明代理用，还可能带来一定的危险性。

2. **[新 V2Ray 白话文指南-透明代理(TPROXY)#设置网关](https://guide.v2fly.org/app/tproxy.html#设置网关)** 中的第三条说：`手动配置 PC 的网络，将默认网关指向树莓派的地址即 192.168.1.22。此时 PC 应当能正常上网（由于还没设置代理，“正常”是指可以上国内的网站）`。实际上，Ubuntu、CentOS、debian 等系统就算开启了 IP 转发，PC 也不能正常上网，这是正常的。事实上只有 OpenWRT 能做到文中所描述的那样，据 **[@BioniCosmos](https://github.com/BioniCosmos)** 点拨，这是由于一般的 Linux 系统没有 Masquery 规则。

3. **[too many open files 问题](https://guide.v2fly.org/app/tproxy.html#解决-too-many-open-files-问题)** ，解决方法见 **[\[透明代理\]通过 gid 规避 Xray 流量-配置最大文件大开数&运行 Xray 客户端](../iptables_gid#3-配置最大文件大开数运行xray客户端)**

4. 避免已有连接的包二次通过 TPROXY ,待补充...

5. 主路由、单臂路由与旁路由，待补充...

---

---
url: /document/level-2/tproxy.md
---
# 透明代理（TProxy）配置教程

本配置基于[TProxy 透明代理的新 V2Ray 白话文教程](https://guide.v2fly.org/app/tproxy.html)，加入了 Xray 的新特性，使用 VLESS + XTLS Vision 方案，并将旧教程中默认出站代理的分流方式改为默认出站直连，使用者请按照实际情况进行修改。

本文中所有配置已在 Raspberry Pi 2B、Ubuntu 20.04 环境下测试成功，如在其它环境中使用请自行调整配置。

## 开始之前

请检查您的设备是否有可用的网络连接，且服务端已经配置成功，客户端已经安装完毕。

需注意的是，目前很多透明代理教程都会将 Linux 系统的 IP 转发打开，但这样会导致 Splice 性能下降。详情请参考[大案牍术破案纪实第三篇--我们是如何破解 Splice 性能下降甚至低于 Direct 之谜的](https://github.com/XTLS/Xray-core/discussions/59)。

这里我想要补充的是，很多透明代理教程会使用 Netfilter 进行分流，使直连流量直接发出而不经过 Xray，这时必须开启 IP 转发；也有的教程，如本文，会将所有流量导入 Xray 之中，由 Xray 的路由模块进行分流，这时无需开启 IP 转发。

## Xray 配置

为了更好的分流体验，请替换默认路由规则文件为 [Loyalsoldier/v2ray-rules-dat](https://github.com/Loyalsoldier/v2ray-rules-dat)，否则 Xray-core 将无法加载本配置。

```bash
sudo curl -oL /usr/local/share/xray/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
sudo curl -oL /usr/local/share/xray/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
```

```json
{
  "log": {
    "loglevel": "warning",
    "error": "/var/log/xray/error.log",
    "access": "/var/log/xray/access.log"
  },
  "inbounds": [
    {
      "tag": "all-in",
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "allowedNetwork": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy"
        }
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIPv4"
      },
      "streamSettings": {
        "sockopt": {
          "mark": 2
        }
      }
    },
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "address": "服务端域名",
        "port": 443,
        "id": "UUID",
        "flow": "xtls-rprx-vision",
        "encryption": "none"
      },
      "streamSettings": {
        "network": "tcp",
        "security": "xtls",
        "sockopt": {
          "mark": 2
        }
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    },
    {
      "tag": "dns-out",
      "protocol": "dns",
      "settings": {
        "rewriteAddress": "8.8.8.8"
      },
      "proxySettings": {
        "tag": "proxy"
      },
      "streamSettings": {
        "sockopt": {
          "mark": 2
        }
      }
    }
  ],
  "dns": {
    "hosts": {
      "服务端域名": "服务端 IP"
    },
    "servers": [
      {
        "address": "119.29.29.29",
        "port": 53,
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      {
        "address": "223.5.5.5",
        "port": 53,
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      "8.8.8.8",
      "1.1.1.1",
      "https+local://doh.dns.sb/dns-query"
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "inboundTag": ["all-in"],
        "port": 53,
        "outboundTag": "dns-out"
      },
      {
        "ip": ["8.8.8.8", "1.1.1.1"],
        "outboundTag": "proxy"
      },
      {
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "domain": ["geosite:geolocation-!cn"],
        "outboundTag": "proxy"
      },
      {
        "ip": ["geoip:telegram"],
        "outboundTag": "proxy"
      }
    ]
  }
}
```

::: tip TIP
本配置会劫持所有发往 53 端口的流量以解决 DNS 污染问题，所以客户端和本机的 DNS 服务器的地址可以随意配置。
:::

## 策略路由配置

```
sudo ip route add local default dev lo table 100 # 添加路由表 100
sudo ip rule add fwmark 1 table 100 # 为路由表 100 设定规则
```

## Netfilter 配置

::: warning 注意
nftables 配置与 iptables 配置二选一，不可同时使用。
:::

```nftables
#!/usr/sbin/nft -f

flush ruleset

define RESERVED_IP = {
    10.0.0.0/8,
    100.64.0.0/10,
    127.0.0.0/8,
    169.254.0.0/16,
    172.16.0.0/12,
    192.0.0.0/24,
    224.0.0.0/4,
    240.0.0.0/4,
    255.255.255.255/32
}

table ip xray {
        chain prerouting {
                type filter hook prerouting priority mangle; policy accept;
                ip daddr $RESERVED_IP return
                ip daddr 192.168.0.0/16 tcp dport != 53 return
                ip daddr 192.168.0.0/16 udp dport != 53 return
                ip protocol tcp tproxy to 127.0.0.1:12345 meta mark set 1
                ip protocol udp tproxy to 127.0.0.1:12345 meta mark set 1
        }
        chain output {
                type route hook output priority mangle; policy accept;
                ip daddr $RESERVED_IP return
                ip daddr 192.168.0.0/16 tcp dport != 53 return
                ip daddr 192.168.0.0/16 udp dport != 53 return
                meta mark 2 return
                ip protocol tcp meta mark set 1
                ip protocol udp meta mark set 1
        }
}
```

::: tip 使用方法

将上述配置写入一个文件（如 `nft.conf`），之后将该文件赋予可执行权限，最后使用 root 权限执行该文件即可（`# ./nft.conf`）。
:::

```bash
iptables -t mangle -N XRAY
iptables -t mangle -A XRAY -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A XRAY -d 100.64.0.0/10 -j RETURN
iptables -t mangle -A XRAY -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A XRAY -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A XRAY -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A XRAY -d 192.0.0.0/24 -j RETURN
iptables -t mangle -A XRAY -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY -d 240.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A XRAY -d 192.168.0.0/16 -p tcp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY

iptables -t mangle -N XRAY_SELF
iptables -t mangle -A XRAY_SELF -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A XRAY_SELF -d 100.64.0.0/10 -j RETURN
iptables -t mangle -A XRAY_SELF -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A XRAY_SELF -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A XRAY_SELF -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A XRAY_SELF -d 192.0.0.0/24 -j RETURN
iptables -t mangle -A XRAY_SELF -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY_SELF -d 240.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY_SELF -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A XRAY_SELF -d 192.168.0.0/16 -p tcp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY_SELF -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY_SELF -m mark --mark 2 -j RETURN
iptables -t mangle -A XRAY_SELF -p tcp -j MARK --set-mark 1
iptables -t mangle -A XRAY_SELF -p udp -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -j XRAY_SELF
```

配置完成后，将局域网内其它设备的默认网关改为该设备 IP，就可以直接翻墙了。在其它主机和本机皆测试成功后，可进行下一步配置。

## 配置永久化与开机自启

首先将已经编辑好的 nftables 配置文件移动到 `/etc` 目录下，并重命名为 `nftables.conf`。然后编辑 `/lib/systemd/system/nftables.service`。

```ini
[Unit]
Description=nftables
Documentation=man:nft(8) http://wiki.nftables.org
Wants=network-pre.target
Before=network-pre.target shutdown.target
Conflicts=shutdown.target
DefaultDependencies=no

[Service]
Type=oneshot
RemainAfterExit=yes
StandardInput=null
ProtectSystem=full
ProtectHome=true
ExecStart=/usr/sbin/nft -f /etc/nftables.conf ; /usr/sbin/ip route add local default dev lo table 100 ; /usr/sbin/ip rule add fwmark 1 table 100
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
ExecStop=/usr/sbin/nft flush ruleset ; /usr/sbin/ip route del local default dev lo table 100 ; /usr/sbin/ip rule del table 100

[Install]
WantedBy=sysinit.target
```

最后 enable 即可。

关于 iptables 的永久化，建议直接安装 `iptables-persistent`。

安装过程中会提示你选择“是否保存配置”，如果已经将 iptables 配置写入系统，那么此时选择“是”即可；如果尚未写入也没有关系，安装完毕后将配置写入，然后执行 `netfilter-persistent save` 即可（需要 root 权限）。

之后编辑 `/lib/systemd/system/netfilter-persistent.service`。

```ini
[Unit]
Description=netfilter persistent configuration
DefaultDependencies=no
Wants=network-pre.target systemd-modules-load.service local-fs.target
Before=network-pre.target shutdown.target
After=systemd-modules-load.service local-fs.target
Conflicts=shutdown.target
Documentation=man:netfilter-persistent(8)

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/netfilter-persistent start ; /usr/sbin/ip route add local default dev lo table 100 ; /usr/sbin/ip rule add fwmark 1 table 100
ExecStop=/usr/sbin/netfilter-persistent stop ; /usr/sbin/ip route flush dev lo table 100 ; /usr/sbin/ip rule del table 100

[Install]
WantedBy=multi-user.target
```

---

---
url: /document/level-2/tproxy_ipv4_and_ipv6.md
---
# TProxy 透明代理（ipv4 and ipv6）配置教程

本配置参考了[TProxy 透明代理的新 V2Ray 白话文教程](https://guide.v2fly.org/app/tproxy.html)，[透明代理（TProxy）配置教程](https://xtls.github.io/document/level-2/tproxy.html#%E5%BC%80%E5%A7%8B%E4%B9%8B%E5%89%8D)以及[透明代理通过 gid 规避 Xray 流量](https://xtls.github.io/document/level-2/iptables_gid.html)，加入了透明代理对 ipv6 的支持，并且使用 VLESS-TCP-XTLS-RPRX-Vision 方案对抗封锁 (推荐使用 1.7.2 及之后版本)。

关于 Xray 的配置并不是本文重点，使用者可依实际情况进行修改，具体可以参考[官方文档示例](https://github.com/XTLS/Xray-examples)或其他优秀示例 比如[@chika0801](https://github.com/chika0801/Xray-examples) 又如[@lxhao61](https://github.com/lxhao61/integrated-examples)。

::: warning 注意

若使用其他配置，你需要着重注意客户端配置中 `outbound` 中`tag` 为 `proxy` 的部分，其他部分不变

服务端配置也要同时改变
:::

此配置意在解决例如 Netflix 等默认使用 ipv6 连接的网站无法通过旁路由进行代理的问题，或对 ipv6 代理有需要。

本文网络结构为单臂旁路由

本文中所有配置已在 Arch Linux (Kernel: 6.0.10) 环境下测试成功，如在其它环境中同理

注意安装相应程序 `# sudo apt install iptables ip6tables` 或 `# sudo apt install nftables`。

若旁路由未安装 xray 程序，可以手动下载相应 xray 程序如 [Xray-linux-64.zip](https://github.com/XTLS/Xray-core/releases/download/v1.7.0/Xray-linux-64.zip) ，然后复制 [install-release.sh](https://github.com/XTLS/Xray-install/blob/main/install-release.sh) 文件到旁路由，赋予可执行权限 `# chmod 700 install-release.sh`，然后使用 `# ./install-release.sh --local Xray-linux-64.zip` 根据提示进行本地安装。

## Xray 配置

### 客户端配置

```json
{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "all-in",
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "allowedNetwork": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"]
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy"
        }
      }
    },
    {
      "port": 10808,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"]
      },
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      //此为默认outbound，路由(routing)模块若未匹配到任何规则，则默认走此 proxy 出口，如果你希望直连国内优先请将下面 direct 出口放到 outbound 第一，看不懂可忽略
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "address": "yourdomain.domain", //改为你自己的域名，直接填写ipv4或ipv6地址也可以
        "port": 443,
        "id": "uuid", //填写uuid，可通过在终端中输入 xray uuid 生成；此处也支持任意字符串（https://xtls.github.io/config/inbounds/vless.html#userobject）
        "encryption": "none",
        "flow": "xtls-rprx-vision"
      },
      "streamSettings": {
        "sockopt": {
          "mark": 255
        },
        "network": "tcp",
        "security": "tls", //注意使用 xtls-rprx-vision 流控此处需为 tls
        "tlsSettings": {
          //注意使用 xtls-rprx-vision 流控此处需为 tlsSettings
          "allowInsecure": false,
          "serverName": "yourdomain.domain", //改为你自己的域名
          "fingerprint": "chrome" //此设置建议先看下Release, https://github.com/XTLS/Xray-core/releases/tag/v1.7.3
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIP"
      },
      "streamSettings": {
        "sockopt": {
          "mark": 255
        }
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    },
    {
      "tag": "dns-out",
      "protocol": "dns",
      "streamSettings": {
        "sockopt": {
          "mark": 255
        }
      }
    }
  ],
  "dns": {
    "hosts": {
      "domain:googleapis.cn": "googleapis.com",
      "dns.google": "8.8.8.8",
      "你的VPS域名": "你的VSP IP" //如果 outbound 的 proxy 里 address 填的域名：希望代理走ipv4，这里 VPS IP 填VPS的ipv4, 希望代理走ipv6，这里VPS IP 填VPS的ipv6；outbound 的 proxy 里 address 填的 IP，这行不用写。
    },
    "servers": [
      "https://1.1.1.1/dns-query",
      {
        "address": "119.29.29.29",
        "domains": ["geosite:cn"],
        "expectIPs": ["geoip:cn"]
      },
      "https://dns.google/dns-query",
      "223.5.5.5",
      "localhost"
    ]
  },
  "routing": {
    "domainMatcher": "mph",
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "inboundTag": ["all-in"],
        "port": 123,
        "network": "udp",
        "outboundTag": "direct"
      },
      {
        "inboundTag": ["all-in"],
        "port": 53,
        "network": "udp",
        "outboundTag": "dns-out"
      },
      {
        "ip": ["119.29.29.29", "223.5.5.5"],
        "outboundTag": "direct"
      },
      {
        "protocol": ["bittorrent"],
        "outboundTag": "direct"
      },
      {
        "ip": ["geoip:private", "geoip:cn"], //此处可加入 VPS IP 避免 ssh 时被代理
        "outboundTag": "direct"
      },
      {
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "ip": ["1.1.1.1", "8.8.8.8"],
        "outboundTag": "proxy"
      },
      {
        "domain": [
          "geosite:geolocation-!cn",
          "domain:googleapis.cn",
          "dns.google"
        ],
        "outboundTag": "proxy"
      }
    ]
  }
}
```

### 服务端配置

```json
{
  "log": {
    "loglevel": "warning"
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        //阻止 cnip 提高安全性，或者可以将 cn 流量导入 warp 中，详见https://xtls.github.io/document/level-2/warp.html
        "ip": ["geoip:cn"],
        "outboundTag": "block"
      }
    ]
  },
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "users": [
          {
            "id": "uuid", //与客户端相同
            "flow": "xtls-rprx-vision"
          }
        ],
        "decryption": "none",
        "fallbacks": [
          {
            "dest": 8080 //回落，需要 web 配合，参见白话文，不设置也行
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "certificates": [
            {
              "certificateFile": "/etc/ssl/private/fullchain.crt",
              "keyFile": "/etc/ssl/private/crt.key" //参照小小白话文将生成的 fullchain.crt 以及 cert.key证书的路径相应填于此处(https://xtls.github.io/document/level-0/ch06-certificates.html#_6-4-%E6%AD%A3%E5%BC%8F%E8%AF%81%E4%B9%A6%E7%94%B3%E8%AF%B7)
            }
          ]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }
  ]
}
```

## Netfilter 配置

### 首先设置策略路由

```bash
# 设置策略路由 v4
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

# 设置策略路由 v6
ip -6 rule add fwmark 1 table 106
ip -6 route add local ::/0 dev lo table 106

# 直连从主路由发出
ip route add default via 192.168.31.1 #写主路由 ipv4, 采用局域网设备上网设置方法一可不写此命令
ip -6 route add default via fd00:6868:6868::1 #写主路由 ipv6, 采用局域网设备上网设置方法一可不写此命令

```

::: tip 使用方法

直接将命令复制到旁路由终端执行
:::

::: tip 关于直连从主路由发出

在旁路由使用命令`ip route show`，如果使用下属方法一，则`default via`后应是主路由 ip，无需更改；如使用下述方法二，则`default via`后应是旁路由 ip，此时直连网站 DNS 解析会回环，造成直连网站无法访问，因此需指定为主路由 ip。
:::

如果是在路由器上指定了默认网关为旁路由（亦即下述“局域网设备上网设置方法二”），那么就需要设置上述 `# 直连从主路由发出` ，除了通过 iproute2 命令行方式设置，也可以通过 dhcpcd 或者 systemctl-network 设置静态 IP，这里以 dhcpcd 为例，编辑 `/etc/dhcpcd.conf` 文件，在最下方加入如下配置，具体 IP 根据你的实际情况修改，其中 `interface` 可以通过 `# ip link show` 查看要设定的网口或者无线设备。

```
interface enp0s25
static ip_address=192.168.31.100/24
static ip6_address=fd00:6868:6868::8888/64
static routers=192.168.31.1
static domain_name_servers=192.168.31.1 fd00:6868:6868::1
```

这样通过静态 IP 设置 IP 及网关后就无需每次开机设置 `# 直连从主路由发出`。

::: warning 注意

以下 nftables 配置与 iptables 配置二选一，不可同时使用。
:::

### 使用 iptables

此处配置将 ipv4 与 ipv6 写在同一文件中。

```bash
# 代理局域网设备 v4
iptables -t mangle -N XRAY
iptables -t mangle -A XRAY -d 127.0.0.1/32 -j RETURN
iptables -t mangle -A XRAY -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A XRAY -d 192.168.0.0/16 -p tcp -j RETURN
iptables -t mangle -A XRAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY -j RETURN -m mark --mark 0xff
iptables -t mangle -A XRAY -p udp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -j XRAY

# 代理局域网设备 v6
ip6tables -t mangle -N XRAY6
ip6tables -t mangle -A XRAY6 -d ::1/128 -j RETURN
ip6tables -t mangle -A XRAY6 -d fe80::/10 -j RETURN
ip6tables -t mangle -A XRAY6 -d fd00::/8 -p tcp -j RETURN
ip6tables -t mangle -A XRAY6 -d fd00::/8 -p udp ! --dport 53 -j RETURN
ip6tables -t mangle -A XRAY6 -j RETURN -m mark --mark 0xff
ip6tables -t mangle -A XRAY6 -p udp -j TPROXY --on-ip ::1 --on-port 12345 --tproxy-mark 1
ip6tables -t mangle -A XRAY6 -p tcp -j TPROXY --on-ip ::1 --on-port 12345 --tproxy-mark 1
ip6tables -t mangle -A PREROUTING -j XRAY6

# 代理网关本机 v4
iptables -t mangle -N XRAY_MASK
iptables -t mangle -A XRAY_MASK -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A XRAY_MASK -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A XRAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN
iptables -t mangle -A XRAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
iptables -t mangle -A XRAY_MASK -j RETURN -m mark --mark 0xff
iptables -t mangle -A XRAY_MASK -p udp -j MARK --set-mark 1
iptables -t mangle -A XRAY_MASK -p tcp -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -j XRAY_MASK

# 代理网关本机 v6
ip6tables -t mangle -N XRAY6_MASK
ip6tables -t mangle -A XRAY6_MASK -d fe80::/10 -j RETURN
ip6tables -t mangle -A XRAY6_MASK -d fd00::/8 -p tcp -j RETURN
ip6tables -t mangle -A XRAY6_MASK -d fd00::/8 -p udp ! --dport 53 -j RETURN
ip6tables -t mangle -A XRAY6_MASK -j RETURN -m mark --mark 0xff
ip6tables -t mangle -A XRAY6_MASK -p udp -j MARK --set-mark 1
ip6tables -t mangle -A XRAY6_MASK -p tcp -j MARK --set-mark 1
ip6tables -t mangle -A OUTPUT -j XRAY6_MASK

# 新建 DIVERT 规则，避免已有连接的包二次通过 TPROXY，理论上有一定的性能提升 v4
iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -I PREROUTING -p tcp -m socket -j DIVERT

# 新建 DIVERT 规则，避免已有连接的包二次通过 TPROXY，理论上有一定的性能提升 v6
ip6tables -t mangle -N DIVERT
ip6tables -t mangle -A DIVERT -j MARK --set-mark 1
ip6tables -t mangle -A DIVERT -j ACCEPT
ip6tables -t mangle -I PREROUTING -p tcp -m socket -j DIVERT

```

::: tip 使用方法

将上述配置写入一个文件（如 `iptables.rules`），之后将该文件赋予可执行权限`# chmod 700 ./iptables.rules`

最后使用 root 权限执行该文件即可：`# ./iptables.rules`或`# source iptables.rules`。
:::

### 使用 nftables

此处合并 ipv4 与 ipv6

```
#!/usr/sbin/nft -f

flush ruleset

table inet xray {
        chain prerouting {
                type filter hook prerouting priority filter; policy accept;
                ip daddr { 127.0.0.0/8, 224.0.0.0/4, 255.255.255.255 } return
                meta l4proto tcp ip daddr 192.168.0.0/16 return
                ip daddr 192.168.0.0/16 udp dport != 53 return
                ip6 daddr { ::1, fe80::/10 } return
                meta l4proto tcp ip6 daddr fd00::/8 return
                ip6 daddr fd00::/8 udp dport != 53 return
                meta mark 0x000000ff return
                meta l4proto { tcp, udp } meta mark set 0x00000001 tproxy ip to 127.0.0.1:12345 accept
                meta l4proto { tcp, udp } meta mark set 0x00000001 tproxy ip6 to [::1]:12345 accept
        }

        chain output {
                type route hook output priority filter; policy accept;
                ip daddr { 127.0.0.0/8, 224.0.0.0/4, 255.255.255.255 } return
                meta l4proto tcp ip daddr 192.168.0.0/16 return
                ip daddr 192.168.0.0/16 udp dport != 53 return
                ip6 daddr { ::1, fe80::/10 } return
                meta l4proto tcp ip6 daddr fd00::/8 return
                ip6 daddr fd00::/8 udp dport != 53 return
                meta mark 0x000000ff return
                meta l4proto { tcp, udp } meta mark set 0x00000001 accept
        }

        chain divert {
                type filter hook prerouting priority mangle; policy accept;
                meta l4proto tcp socket transparent 1 meta mark set 0x00000001 accept
        }
}

```

::: tip 使用方法

将上述配置写入一个文件（如 `nftables.rules`），之后将该文件赋予可执行权限`# chmod 700 ./nftables.rules`

最后使用 root 权限执行该文件即可：`# ./nftables.rules`或`# source nftables.rules`
:::

其中，网关地址`192.168.0.0/16`, `fd00::/8`等可由`ip address | grep -w inet | awk '{print $2}'`以及`ip address | grep -w inet6 | awk '{print $2}'`[获得](https://xtls.github.io/document/level-2/iptables_gid.html#_4-%E8%AE%BE%E7%BD%AE-iptables-%E8%A7%84%E5%88%99)

或者在 windows 网络设置中查看。

又或者在路由器“上网设置”中查看。

如果前缀`192.168`, `fd00:`相同可不更改，若不同如 `fc00:`, `fe00:` 等则更改为相应值，写法可通过 Goolge 搜索得到如 `fc00::/7`, `fe00::/9`。

### 开机自动运行 Netfilter 配置

首先确认已经运行过上述相应 Netfilter 命令，并且成功测试透明代理配置，以确保接下来输出正确的文件。

#### 若使用 iptables 配置

1. 首先通过 `# iptables-save > /root/iptables.rulesv4` `# ip6tables-save > /root/iptables.rulesv6` 将 iptables 配置写入 `iptables.rulesv4` 和 `iptables.rulesv6` 文件中

2. 然后在 `/etc/systemd/system/` 目录下创建一个名为 `tproxyrules.service` 的文件，添加以下内容并保存

```
[Unit]
Description=Tproxy rules

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c 'until ping -c1 192.168.31.1; do sleep 1; done;'
ExecStart=/sbin/ip rule add fwmark 1 table 100 ; \
/sbin/ip -6 rule add fwmark 1 table 106 ; \
/sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; \
/sbin/ip -6 route add local ::/0 dev lo table 106 ; \
/sbin/ip route add default via 192.168.31.1 ; \
/sbin/ip -6 route add default via fd00:6868:6868::1 ; \
/sbin/iptables-restore /root/iptables.rulesv4 ; \
/sbin/ip6tables-restore /root/iptables.rulesv6
ExecStop=/sbin/ip rule del fwmark 1 table 100 ; \
/sbin/ip -6 rule del fwmark 1 table 106 ; \
/sbin/ip route del local 0.0.0.0/0 dev lo table 100 ; \
/sbin/ip -6 route del local ::/0 dev lo table 106 ; \
/sbin/ip route del default via 192.168.31.1 ; \
/sbin/ip -6 route del default via fd00:6868:6868::1 ; \
/sbin/iptables -t mangle -F ; \
/sbin/ip6tables -t mangle -F

[Install]
WantedBy=multi-user.target
```

3. 最后执行 `systemctl enable tproxyrules` 命令。

#### 如果使用 nftables 配置

1. 首先通过 `# nft list ruleset > /root/nftables.rulesv46` 将 nftables 配置写入 `nftables.rulesv46` 文件中

2. 在 `/etc/systemd/system/` 目录下创建一个名为 `tproxyrules.service` 的文件，然后添加以下内容并保存

```
[Unit]
Description=Tproxy rules

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c 'until ping -c1 192.168.31.1; do sleep 1; done;'
ExecStart=/sbin/ip rule add fwmark 1 table 100 ; \
/sbin/ip -6 rule add fwmark 1 table 106 ; \
/sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; \
/sbin/ip -6 route add local ::/0 dev lo table 106 ; \
/sbin/ip route add default via 192.168.31.1 ; \
/sbin/ip -6 route add default via fd00:6868:6868::1 ; \
/sbin/nft -f /root/nftables.rulesv46 ;
ExecStop=/sbin/ip rule del fwmark 1 table 100 ; \
/sbin/ip -6 rule del fwmark 1 table 106 ; \
/sbin/ip route del local 0.0.0.0/0 dev lo table 100 ; \
/sbin/ip -6 route del local ::/0 dev lo table 106 ; \
/sbin/ip route del default via 192.168.31.1 ; \
/sbin/ip -6 route del default via fd00:6868:6868::1 ; \
/sbin/nft flush ruleset

[Install]
WantedBy=multi-user.target
```

3. 最后执行 `systemctl enable tproxyrules` 命令。

::: tip tproxyrules.service

注意其中主路由器 IP 地址，根据实际修改

`ExecStartPre=/bin/sh -c 'until ping -c1 192.168.31.1; do sleep 1; done;'` 命令为确保获得 IP 地址后再执行命令，否则会诡异报错，其中 IP 地址为主路由器地址，根据实际修改。
:::

::: warning 注意

如果通过 dhcpcd 等设置了静态 IP 及网关，则上述相关 `ip route add/del` 设置需删除
:::

## 局域网设备上网设置

此处假定旁路由 ipv4, ipv6 地址分别为`192.168.31.100`, `fd00:6868:6868::8866`, 旁路由的 ipv4, ipv6 地址可由命令`ip add`获得。

### 方法一

局域网设备上网有两种方式，第一种就是在使用设备上进行静态 IP 的配置，将网关指向旁路由 IP。注意绝大部分手机仅支持手动配置 ipv4 网关，不支持手动配置 ipv6 网关，除非 root 后进行相关设置。

以 windows 设备为例，可以先开启 DHCP 记录自动分配的 IP 以参考，然后手写静态配置。

::: tip DNS 设置

此配置劫持 DNS 流量，DNS 可以随便写

建议设置为旁路由 IP，防止 DNS 泄露
:::

&#x20;

### 方法二

局域网设备上网的第二种方式，是在路由器上进行网关设置，这种方法对于连接到此路由器的设备无需做任何设置即可科学上网，但注意有些路由器不支持 ipv6 的网关设置，有 ipv6 需求的设备仍需在所需设备上单独手动配置 ipv6 相关设置参考方法一。

## Finally

按照以上方法设置后设备即可双栈访问，进入测试网站比如 https://ipv6-test.com/ 可以看到如下结果 (需要代理此网站才能看到如下结果)

## 写在最后

如今 ipv6 并未完全普及，我们日常访问的流量 99%仍为 ipv4 流量；很多 VPS 商家虽然提供 ipv6 地址，但线路优化非常垃圾，甚至处于不可用状态，为何要加入 ipV6 的设置？

可以看到目前 ipv6 处于很尴尬的境地，各种设备对于 ipv6 的支持很烂，但是都在逐步完善，同时 Windows 系统对于 ipv6 的优先级也在提高，很多浏览器也会优先进行 ipv6 的解析以及访问，很多网站也开始默认使用 ipv6 进行访问（比如 Netflix, 如果没有配置 ipv6, 浏览器打开 Netflix 会显示 Not Available 是因为没有代理 Netflix 的 ipv6 请求，当然可以选择禁用 Windows 的 ipv6，但支持 ipv6 的 pt 站就无法使用）

这种情况下 ipv4 无法完全胜任网络冲浪的需求，即使是那 1%的流量，遇到了也会让人头疼不已。

而可以预见 ipv6 也会逐步与 ipv4 分庭抗礼，所以有必要加入 ipv6 的设置。

---

---
url: /document/level-2/nginx_or_haproxy_tls_tunnel.md
---
# Nginx 或 Haproxy 搭建 TLS 隧道隐藏指纹

Nginx 或 Haproxy 实现的 HTTPS 隧道、HTTP/2 over HTTPS 隧道、WebSocket over HTTP/2 over HTTPS 隧道、gRPC over HTTP/2 over HTTPS 隧道以及自签证书双端认证的 gRPC over HTTP/2 over HTTPS 隧道

## 客户端服务端 Nginx 构建 HTTPS 隧道隐藏指纹

网路结构：

xray\_client ---tcp--- nginx\_client ---HTTPS--- nginx\_sever ---tcp--- xray\_server

## 编译 nginx --with-stream

在客户端及服务端均编译

`curl -O -L http://nginx.org/download/nginx-1.22.1.tar.gz`

`tar -zxvf nginx-1.22.1.tar.gz`

`cd nginx-1.22.1`

`apt install gcc make` //编译依赖 gcc 以及 make

`./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_v2_module --with-stream --with-stream_ssl_module` //此步需要依赖一些库，根据报错安装相应 lib

`make && make install`

编译之后 nginx 文件夹位于 `/usr/local/nginx`

## 配置 nginx

编辑 nginx 配置文件 nginx.conf

`vim /usr/local/nginx/conf/nginx.conf`

服务端加入如下配置

服务器申请证书不再赘述，参考[白话文](../level-0/ch06-certificates.md)

```
stream {
    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_protocols TLSv1.3;
        ssl_certificate /path/to/cert/domain.crt; # crt 文件位置
        ssl_certificate_key /path/to/cert/domain.key; # key 文件位置
        proxy_pass unix:/dev/shm/vless.sock; # 使用 domain socket
    }
}
```

::: warning 注意

stream 部分与 http 模块并列，客户端可删除 http 部分，服务端可删除或搭建网页伪装回落
:::

客户端加入如下配置

```
stream {
    server {
        listen 6666;
        listen [::]:6666;
        proxy_ssl on;
        proxy_ssl_protocols TLSv1.3;
        proxy_ssl_server_name on;
        proxy_ssl_name yourdomain.domain; # 服务器域名
        proxy_pass ip:443; # 服务器 ip 形如 proxy_pass 6.6.6.6:443; 或 proxy_pass [2401:0:0::1]:443;
    }
}
```

在 `/etc/systemd/system` 文件夹中创建 `nginx.service` 文件

`vim /etc/systemd/system/nginx.service`

写入如下

```
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
After=xray.service

[Service]
Type=forking
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

加入开机自启

`systemctl enable nginx`

## xray 配置

服务端 xray 配置

```json
{
  "log": {
    "loglevel": "none"
  },
  "inbounds": [
    {
      "listen": "/dev/shm/vless.sock,0666",
      "protocol": "vless",
      "settings": {
        "users": [
          {
            "id": "uuid"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "tcp"
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
```

客户端 xray 配置，此处以旁路由透明代理为例

```json
{
  "log": {
    "loglevel": "none"
  },
  "dns": {
    "servers": [
      "1.1.1.1",
      {
        "address": "119.29.29.29",
        "domains": ["geosite:cn"],
        "expectIP": ["geoip:cn"]
      }
    ],
    "disableFallback": true,
    "disableFallbackIfMatch": true
  },
  "inbounds": [
    {
      "tag": "tproxy-in",
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "allowedNetwork": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy",
          "mark": 255
        }
      }
    },
    {
      "tag": "http",
      "port": 10808,
      "listen": "127.0.0.1",
      "protocol": "http",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "tag": "nginxtls",
      "protocol": "vless",
      "settings": {
        "address": "127.0.0.1",
        "port": 6666,
        "id": "uuid",
        "encryption": "none"
      },
      "streamSettings": {
        "sockopt": {
          "mark": 255
        },
        "network": "tcp"
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "streamSettings": {
        "sockopt": {
          "mark": 255
        }
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    }
  ],
  "routing": {
    "domainMatcher": "mph",
    "domainStrategy": "AsIs",
    "rules": [
      {
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "port": 123,
        "network": "udp",
        "outboundTag": "direct"
      },
      {
        "ip": ["1.1.1.1"],
        "outboundTag": "proxy"
      },
      {
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "protocol": ["bittorrent"],
        "outboundTag": "direct"
      },
      {
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "inboundTag": ["tproxy-in"],
        "outboundTag": "nginxtls"
      }
    ]
  }
}
```

如果使用透明代理需要在 iptables 或 ip6tables 配置中加入

```
# 设置策略路由 v4
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

# 设置策略路由 v6
ip -6 rule add fwmark 1 table 106
ip -6 route add local ::/0 dev lo table 106

# VPS IP 直连
iptables -t mangle -A XRAY_MASK -d VSP_IPv4/32 -j RETURN
ip6tables -t mangle -A XRAY6_MASK -d VPS_IPv6/128 -j RETURN
```

## 客户端及服务端启动服务

`systemctl restart xray`

`systemctl restart nginx`

## 结束

# 双端 Haproxy 构建 HTTPS 隧道隐藏指纹

安装 Haproxy

`pacman -Su haproxy` 或 `apt install haproxy`

Haproxy 处理 ssl 需要 openssl 支持，检查 openssl 版本，必要时安装或更新

## HTTPS 隧道

前述 Nginx HTTPS 隧道 Hproxy 同样可以简单做到

网路结构：

xray\_client ---tcp--- haproxy\_client ---HTTPS--- haproxy\_sever ---tcp--- xray\_server

### haproxy\_client 配置 (运行前去掉注释)

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    # 隧道强制使用 TLS 1.3
    ssl-default-server-options ssl-min-ver TLSv1.3

defaults
    log global
    mode tcp
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend xray
    bind 127.0.0.1:6666 # 监听本机 6666 端口
    default_backend tunnel

backend tunnel
    server tunnel www.example.com:443 ssl verify none sni req.hdr(host) alpn h2,http/1.1
    # 域名或 IP 均可以，若填域名建议在 hosts 中指定 IP 降低解析时间；alpn 与服务器协商，服务器端为 alpn h2,http1.1 时，客户端指定为 h2 则隧道为 HTTP2 方式连接，指定为 http1.1 为 HTTP 方式，双端均写优先 h2
```

### haproxy\_server 配置 (运行前去掉注释)

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    # 指定安全套件并指定 ssl 版本最低 1.2 增加真实性
    ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2

defaults
    log global
    mode tcp
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend tls-in
    bind :::443 ssl crt /path/to/pem alpn h2,http/1.1 # haproxy 使用 pem 进行 ssl 解密，pem 由 cat www.example.com.crt www.example.com.key > www.example.com.pem 获得
    default_backend xray
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP
    use_backend web if HTTP

backend xray
    server xray /dev/shm/vless.sock # 支持 abstract 格式： "abns@vless.sock" ；loopback 方式：127.0.0.1:6666

backend web
    server web /dev/shm/h1h2c.sock # 回落到网页
```

### xray 配置

同上 nginx 部分：最简单的 TCP 配置，可搭配任意协议，建议使用 VLESS+TCP 无需多余加密，参考文档或其他示例

## WebSocket over HTTP/2

Haproxy 支持 HTTP/2 的 h2c 进站及出站

然而援引 xray 文档 HTTP/2 的说明

“由 HTTP/2 的建议，客户端和服务器必须同时开启 TLS 才可以正常使用这个传输方式。...... 当前版本的 HTTP/2 的传输方式并不强制要求入站（服务端）有 TLS 配置。”

即入站可以使用 h2c，出站并不支持 h2c。因此无法使用 xray\_client ---h2c--- haproxy\_client ---HTTP/2+TLS--- haproxy\_sever ---h2c--- xray\_server

但是可以通过 ws 偷个鸡，Haproxy 支持 ws over HTTP/2

则网络结构：xray\_client ---ws--- haproxy\_client ---ws over HTTP/2 over HTTPS--- haproxy\_sever ---ws--- xray\_server

### haproxy\_client 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    # 调整 HTTP/2 的性能，当遇到 HTTP/2 性能问题时都可以设置相关项，更多设置见 Haproxy 文档 tune.h2 部分 https://docs.haproxy.org/2.7/configuration.html
    tune.h2.initial-window-size 536870912 # 初始窗口大小，建议设置，默认值 65536 单位 byte，此值在突发大流量情况下需要一定加载时间，建议根据网速调整
    tune.h2.max-concurrent-streams 512 # 复用线路数，可根据情况设置，默认值 100，一般不用设置(官方不建议改动)

    ssl-default-server-options ssl-min-ver TLSv1.3

defaults
    log global
    mode http
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend xray
    bind 127.0.0.1:6666
    default_backend tunnel

backend tunnel
    server tunnel www.example.com:443 ssl verify none sni req.hdr(host) ws h2 alpn h2
    # ws over HTTP/2
```

### haproxy\_server 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    # 客户端配置即可，服务端配置也无妨
    tune.h2.initial-window-size 536870912
    tune.h2.max-concurrent-streams 512

    ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2

defaults
    log global
    mode http
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend tls-in
    bind :::443 ssl crt /path/to/pem alpn h2,http/1.1
    use_backend xray if { ssl_fc_alpn -i h2 } { path_beg /tunnel }
    use_backend server1 if { ssl_fc_alpn -i h2 } { path_beg /path1 }
    use_backend server2 if { ssl_fc_alpn -i h2 } { path_beg /path2 }
    use_backend server3 if { ssl_fc_alpn -i h2 } { path_beg /path3 }
    default_backend web
    # haproxy 使用 http 模式可以根据 path 分流

backend xray
    server xray abns@vless.sock ws h1

backend server1
    server server1 abns@server1.sock ws h1

backend server2
    server server2 abns@server2.sock ws h1

backend server3
    server server3 abns@server3.sock ws h1

backend web
    server web /dev/shm/h1h2c.sock
```

### xray 配置

简单的 websocket 配置即可，无需 TLS， 配置见 xray 文档示例，配置 "path" 可以用于服务端 haproxy 分流（客户端有分流需求同样可以通过客户端 haproxy 进行，原理类似，参考服务端的 path 分流配置）

## gRPC over HTTP/2

虽然双端的 h2c 不行，但是 gRPC 不要求必须 TLS，直接冲

网络结构：xray\_client ---gRPC h2c--- haproxy\_client ---gRPC over HTTP/2 over HTTPS--- haproxy\_sever ---gRPC h2c--- xray\_server

### haproxy\_client 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    tune.h2.initial-window-size 536870912
    tune.h2.max-concurrent-streams 512

    ssl-default-server-options ssl-min-ver TLSv1.3

defaults
    log global
    mode http
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend xray
    bind 127.0.0.1:6666 proto h2 # 指定 proto h2 使用 h2c
    default_backend tunnel

backend tunnel
    server tunnel www.example.com:443 ssl verify none sni req.hdr(host) alpn h2
```

### haproxy\_server 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    tune.h2.initial-window-size 536870912
    tune.h2.max-concurrent-streams 512

    ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2

defaults
    log global
    mode http
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend tls-in
    bind :::443 ssl crt /path/to/pem alpn h2,http/1.1
    use_backend xray if { ssl_fc_alpn -i h2 } { path_beg /tunnel } # xray gRPC 中配置的 "serviceName" 在 harpoxy 中可以使用 path 进行分流，为方便使用 "multiMode"，使用 path_beg 参数匹配路径
    use_backend server1 if { ssl_fc_alpn -i h2 } { path_beg /path1 }
    use_backend server2 if { ssl_fc_alpn -i h2 } { path_beg /path2 }
    use_backend server3 if { ssl_fc_alpn -i h2 } { path_beg /path3 }
    default_backend web

backend xray
    server xray abns@vless.sock proto h2

backend server1
    server server1 abns@server1.sock proto h2

backend server2
    server server2 abns@server2.sock proto h2

backend server3
    server server3 abns@server3.sock proto h2

backend web
    server web /dev/shm/h1h2c.sock
```

### xray 配置

简单的 gRPC 配置，无需 TLS，配置见文档，配置的 serviceName 可用于分流。

# Haproxy 使用自签证书进行双端认证（gRPC 示例）

这里使用自签证书双端认证加强隧道安全性（但会牺牲一点延迟，不过使用 gRPC 后感知不强），而服务端同时处理信任的证书和自签名证书，并据此分流伪装网站和隧道流量

其中 www.example.com 为伪装站信任证书（如白话文中申请的证书）

tunnel.example.com 为自签证书网址，自签证书可以参考 https://learn.microsoft.com/zh-cn/azure/application-gateway/self-signed-certificates

根证书 ca.crt 服务器证书 server.crt 服务器密钥 server.key

至少需要生成一个 server.pem，客户端可以同样使用此证书用于双端认证；或者生成两个证书，一个 client，一个 server，用于双端认证

需准备 fullchain.crt 用于认证（ cat server.crt ca.crt > fullchain.crt ），server.pem （ cat server.crt server.key ca.crt > server.pem ）用于解密

### haproxy\_client 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    tune.h2.initial-window-size 536870912
    tune.h2.max-concurrent-streams 512

    ssl-default-server-options ssl-min-ver TLSv1.3

defaults
    log global
    mode http
    timeout connect 5s
    timeout client 300s
    timeout server 300s

frontend xray
    bind 127.0.0.1:6666 proto h2
    default_backend tunnel

backend tunnel
    server tunnel tunnel.example.com:443 tfo allow-0rtt ssl crt /path/to/client.pem verify required ca-file /path/to/fullchain.crt sni str(tunnel.example.com) alpn h2
    # 网址自定义，和自签证书一致即可，hosts 中配置 IP 解析，sni 的 str 设定 sni，用于服务端识别
```

### haproxy\_server 配置

```
global
    log /dev/log local0 alert
    log /dev/log local1 alert
    stats socket /dev/shm/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user root
    group root
    daemon

    tune.h2.initial-window-size 536870912
    tune.h2.max-concurrent-streams 512

    ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2

defaults
    log global
    mode http
    timeout connect 5s
    timeout client  300s
    timeout server  300s

frontend tls-in
    bind :::443 tfo allow-0rtt ssl crt /path/to/server.pem verify optional ca-file /path/to/fullchain.crt crt /path/to/www.example.com.pem alpn h2,http/1.1
    use_backend xray if { ssl_fc_sni tunnel.example.com } { ssl_c_used } { ssl_fc_alpn -i h2 } { path_beg /tunnel }
    use_backend server1 if { ssl_fc_sni atunnel.example.com } { ssl_c_used }  { ssl_fc_alpn -i h2 } { path_beg /path2 }
    use_backend server2 if { ssl_fc_sni btunnel.example.com } { ssl_c_used }  { ssl_fc_alpn -i h2 } { path_beg /path3 }
    use_backend server3 if { ssl_fc_sni ctunnel.example.com } { ssl_c_used }  { ssl_fc_alpn -i h2 } { path_beg /path4 }
    default_backend web
    # Haproxy 支持多个 pem 解密
    # 可根据多个客户端的不同 sni 分流，也可以 path 分流，方式多样，更多 acl 见 Haproxy 文档

backend xray
    server xray abns@vless.sock proto h2

backend server1
    server server1 abns@server1.sock proto h2

backend server2
    server server2 abns@server2.sock proto h2

backend server3
    server server3 abns@server3.sock proto h2

backend web
    server web /dev/shm/h1h2c.sock
```

### xray 配置

简单的 gRPC 配置，无需 TLS，配置见文档，配置的 serviceName 可用于分流。

---

---
url: /document/level-2/iptables_gid.md
---
# 透明代理通过 gid 规避 Xray 流量

在现有的 iptables 透明代理白话文(**[新 V2Ray 白话文指南-透明代理](https://guide.v2fly.org/app/transparent_proxy.html)** 、 **[新 V2Ray 白话文指南-透明代理(TPROXY)](https://guide.v2fly.org/app/tproxy.html)** 、 **[透明代理（TProxy）配置教程](./tproxy)**)教程中，对 Xray 流量的规避处理是打 mark 实现的。即对 Xray 出站流量打 mark，通过设置 iptables 规则对对应 mark 的流量直连，来规避 Xray 流量，防止回环。

这么做有以下几个问题：

1. **[莫名流量进入 PREROUTING 链](https://github.com/v2ray/v2ray-core/issues/2621)**

2. 安卓系统有自己的 mark 机制，该方案在安卓上不可用

本教程的方案不需要设置 mark，理论性能更高，同时也不存在上述问题。

## 思路

tproxy 流量只能被 root 权限用户(uid==0)或其他有 CAP\_NET\_ADMIN 权限的用户接收。

iptables 规则可以通过 uid(用户 id)和 gid(用户组 id)分流。

让 Xray 运行在一个 uid==0 但 gid!=0 的用户上，设置 iptables 规则不代理该 gid 的流量来规避 Xray 流量。

## 配置过程

### 1. 前期准备

**安卓系统**

1. 系统已 root

2. 安装 **[busybox](https://play.google.com/store/apps/details?id=stericson.busybox)**

3. 有一个可以执行命令的终端，可以使用 adb shell，termux 等。

**其它 Linux 系统**

需要依赖 sudo，iptables 的 tproxy 模块和 extra 模块。

一般系统都有自带，openwrt 运行：

```bash
opkg install sudo iptables-mod-tproxy iptables-mod-extra
```

另附上一些 openwrt 常用的依赖，缺少可能导致 Xray 无法运行

```bash
opkg install libopenssl ca-certificates
```

### 2. 添加用户(安卓用户请忽略)

安卓系统不支持/etc/passwd 文件来管理用户，请忽略，直接下一步。

```bash
grep -qw xray_tproxy /etc/passwd || echo "xray_tproxy:x:0:23333:::" >> /etc/passwd
```

其中 xray\_tproxy 是用户名，0 是 uid，23333 是 gid，用户名和 gid 可以自己定，uid 必须为 0。
检查用户是否添加成功，运行

```bash
sudo -u xray_tproxy id
```

显示的结果应该是 uid 为 0，gid 为 23333

### 3. 配置运行 Xray，配置 iptables 规则

在现有的 iptables 透明代理白话文(**[新 V2Ray 白话文指南-透明代理](https://guide.v2fly.org/app/transparent_proxy.html)** 、 **[新 V2Ray 白话文指南-透明代理(TPROXY)](https://guide.v2fly.org/app/tproxy.html)** 、 **[透明代理（TProxy）配置教程](./tproxy)**)教程的基础上修改：

1. 修改 json 配置文件，删除 mark 相关内容

2. 修改 iptables 规则，删除 mark 相关内容，并在 OUTPUT 链应用规则处添加选项"-m owner ! --gid-owner 23333"。

如：

```bash
iptables -t mangle -A OUTPUT -j XRAY_SELF
```

改为

```bash
iptables -t mangle -A OUTPUT -m owner ! --gid-owner 23333 -j XRAY_SELF
```

3. 修改运行 Xray 的方式，使其运行在 uid 为 0，gid 为 23333 的用户上，参考[这里](#_3-配置最大文件打开数-运行-xray-客户端)。

## 下面提供一个实现 tproxy 全局代理的完整配置过程

### 1. 完成上面的 前期准备和 添加用户

### 2. 准备 Xray 配置文件

配置 Xray 任意门监听 12345，开启 followRedirect 和 tproxy，不需要设置 sniffing：

```json
{
  "inbounds": [
    {
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "allowedNetwork": "tcp,udp",
        "followRedirect": true
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy"
        }
      }
    }
  ],
  "outbounds": [
    {
      你的服务器配置
    }
  ]
}
```

### 3. 配置最大文件打开数&运行 Xray 客户端

关于最大文件打开数问题见： **[too many open files 问题](https://guide.v2fly.org/app/tproxy.html#解决-too-many-open-files-问题)**

目前 Xray 服务端使用官方脚本安装的已经自动配置了最大文件打开数，无需再修改。

**安卓系统**

```bash
ulimit -SHn 1000000
setuidgid 0:23333 "运行Xray的命令"&
```

**其它 Linux 系统**

```bash
ulimit -SHn 1000000
sudo -u xray_tproxy "运行Xray的命令"&
```

例如：

```bash
ulimit -SHn 1000000
sudo -u xray_tproxy xray -c /etc/xray/config.json &
```

*第一条命令：*

改变最大打开文件数，只对当前终端有效，每次启动 Xray 前都要运行，该命令是设置客户端的最大文件打开数

*第二条命令：*

以 uid 为 0，gid 不为 0 的用户来运行 Xray 客户端，后面加&代表放在后台运行

**检查最大文件打开数是否设置成功**

```bash
cat /proc/Xray的pid/limits
```

找到 max open files 一项，应该是你设置的数值。pid 的获取方法为运行`ps`或`ps -aux`或`ps -a`或`pidof xray`

服务端和客户端都要检查

### 4. 设置 iptables 规则

**代理 ipv4**

```bash
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

# 代理局域网设备
iptables -t mangle -N XRAY
#  "网关所在ipv4网段" 通过运行命令"ip address | grep -w inet | awk '{print $2}'"获得，一般有多个
iptables -t mangle -A XRAY -d 网关所在ipv4网段1 -j RETURN
iptables -t mangle -A XRAY -d 网关所在ipv4网段2 -j RETURN
...

# 组播地址/E类地址/广播地址直连
iptables -t mangle -A XRAY -d 224.0.0.0/3 -j RETURN


# 如果网关作为主路由，则加上这一句，见：https://xtls.github.io/document/level-2/transparent_proxy/transparent_proxy.html#iptables-透明代理的其它注意事项
# 网关LAN_IPv4地址段，运行命令"ip address | grep -w "inet" | awk '{print $2}'"获得，是其中的一个
iptables -t mangle -A XRAY ! -s 网关LAN_IPv4地址段 -j RETURN

# 给 TCP 打标记 1，转发至 12345 端口
# mark只有设置为1，流量才能被Xray任意门接受
iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 应用规则
iptables -t mangle -A PREROUTING -j XRAY

# 代理网关本机
iptables -t mangle -N XRAY_MASK
iptables -t mangle -A XRAY_MASK -m owner --gid-owner 23333 -j RETURN
iptables -t mangle -A XRAY_MASK -d 网关所在ipv4网段1 -j RETURN
iptables -t mangle -A XRAY_MASK -d 网关所在ipv4网段2 -j RETURN
...
iptables -t mangle -A XRAY_MASK -d 224.0.0.0/3 -j RETURN
iptables -t mangle -A XRAY_MASK -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -p tcp -j XRAY_MASK
iptables -t mangle -A OUTPUT -p udp -j XRAY_MASK
```

**代理 ipv6(可选)**

```bash
ip -6 rule add fwmark 1 table 106
ip -6 route add local ::/0 dev lo table 106

# 代理局域网设备
ip6tables -t mangle -N XRAY6
# "网关所在ip6网段" 通过运行命令"ip address | grep -w inet6 | awk '{print $2}'"获得。
ip6tables -t mangle -A XRAY6 -d 网关所在ipv6网段1 -j RETURN
ip6tables -t mangle -A XRAY6 -d 网关所在ipv6网段2 -j RETURN
...

# 如果网关作为主路由，则加上这一句，见：https://xtls.github.io/document/level-2/transparent_proxy/transparent_proxy.html#iptables-透明代理的其它注意事项
# 网关LAN_IPv6地址段，运行命令"ip address | grep -w "inet6" | awk '{print $2}'"获得，是其中的一个
ip6tables -t mangle -A XRAY6 ! -s 网关LAN_IPv6地址段 -j RETURN

ip6tables -t mangle -A XRAY6 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
ip6tables -t mangle -A XRAY6 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
ip6tables -t mangle -A PREROUTING -j XRAY6

# 代理网关本机
ip6tables -t mangle -N XRAY6_MASK
ip6tables -t mangle -A XRAY6_MASK -m owner --gid-owner 23333 -j RETURN
ip6tables -t mangle -A XRAY6_MASK -d 网关所在ipv6网段1 -j RETURN
ip6tables -t mangle -A XRAY6_MASK -d 网关所在ipv6网段2 -j RETURN
...
ip6tables -t mangle -A XRAY6_MASK -j MARK --set-mark 1
ip6tables -t mangle -A OUTPUT -p tcp -j XRAY6_MASK
ip6tables -t mangle -A OUTPUT -p udp -j XRAY6_MASK
```

---

---
url: /document/level-2/redirect.md
---
# 基于 fwmark 或 sendThrough 的流量重定向

通过 Xray 将特定的流量指向特定出口，实现全局路由“分流”

## 前言

之前在网络上看到许多代理或者 VPN 会接管全局路由，如果与 Xray 同时安装，会导致 Xray 失效。参考了网络上许多教程，及时分流，也是通过维护一张或者多张 CIDR
路由表来实现的。这种情况下并不优雅，如果我想可以任意替换，实现按需分流，那有没有更好的办法呢？有！

通过 fwmark 或 Xray 的 sendThrough/sockopt.interface，再简单配合路由表功能即可实现：

1. Xray 可设置指定的 Tag、域名等走指定接口。如果您的接口是双栈的，可以指定 IPV4 或者 IPV6
2. 其余用户则走原 IPV4 或者 IPV6

具体设置如下（以 Debian10 为例）：

## 1、安装代理或者 VPN 软件（例如 Wireguard、IPsec 等）

根据不同系统和不同软件，请参考官方安装方法

## 2、编辑 VPN 配置文件（以 WireGuard 为例）

原始文件：

```ini
[Interface]
PrivateKey = <PriKey>
Address = <IPv4>
Address = <IPv6>
DNS = 8.8.8.8
MTU = 1280
[Peer]
PublicKey = <Pubkey>
AllowedIPs = ::/0
AllowedIPs = 0.0.0.0/0
Endpoint = <EndpointIP>:<Port>
```

在 `[Interface]` 下添加如下命令：

```ini
Table = <table>
### fwmark
PostUP = ip rule add fwmark <mark> lookup <table>
PostDown = ip rule del fwmark <mark> lookup <table>
PostUP = ip -6 rule add fwmark <mark> lookup <table>
PostDown = ip -6 rule del fwmark <mark> lookup <table>
## sendThrough
PreUp = ip rule add from <IPv4> lookup <table>
PostDown = ip rule del from <IPv4> lookup <table>
PreUp = ip -6 rule add from <IPv6> lookup <table>
PostDown = ip -6 rule del from <IPv6> lookup <table>
## sockopt.interface
PreUp = ip rule add oif %i lookup <table>
PostDown = ip rule del oif %i lookup <table>
PreUp = ip -6 rule add oif %i lookup <table>
PostDown = ip -6 rule del oif %i lookup <table>
```

::: tip

* 此配置文件融合了 `fwmark` / `sendThrough` / `sockopt.interface`，表示
* 送入此设备 `%i` 的连接 / 送入此 `<IPv4/6>` 的连接 / `fwmark` 被标记为 `<mark>` 的连接
* 将会使用 wireguard 进行转发
* `%i` 是 wireguard 配置文件中的占位符，表示在启动时替换为这个设备的名称
  :::

保存

可顺手安装

::: warning
如果使用了 `[Interface]` 中的 `DNS` 字段，这个程序将会是必须的
:::

```bash
apt install openresolv
```

## 3、启用 WireGuard 网络接口

加载内核模块

```bash
modprobe wireguard
```

检查 WG 模块加载是否正常

```bash
lsmod | grep wireguard
```

## 4、Xray-core 配置文件修改

```json
{
  "api": {
    "services": [
      "HandlerService",
      "LoggerService",
      "StatsService"
    ],
    "tag": "api"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": <port>,
      "protocol": "dokodemo-door",
      "settings": {
        "rewriteAddress": "127.0.0.1"
      },
      "tag": "api"
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIPv4"
      }
      //修改此处，可v4或者v6
    },
    //            <--请在不同的方案中选择-->   方案1：fwmark
    {
      "protocol": "freedom",
      "tag": "wg0",
      "streamSettings": {
        "sockopt": {
          "mark": // <mark>
        }
      },
      "settings": {
        "domainStrategy": "UseIPv6"
      }
    },  //设置fwmark为<mark>的用户走指定方式”UseIPv6””UseIPv4”
    //            <--请在不同的方案中选择-->   方案2：sendThrough
    {
      "tag": "wg0",
      "protocol": "freedom",
      "sendThrough": "your wg0 v4 address",
      //修改此处，可v4或者v6
      "settings": {
        "domainStrategy": "UseIPv4"
      }
      //修改此处，可v4或者v6
    },
    //            <--请在不同的方案中选择-->   方案3：sockopt.interface
    {
      "tag": "wg0",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIPv4"
      },
      "streamSettings": {
        "sockopt": {
          "interface": "wg0"
        }
      }
    },
    //            <--请在不同的方案中选择-->   结束
    {
      "protocol": "blackhole",
      "settings": {},
      "tag": "blocked"
    }
  ],
  "policy": {
    "system": {
      "statsInboundDownlink": true,
      "statsInboundUplink": true
    }
  },
  "routing": {
    "rules": [
      {
        "inboundTag": [
          "api"
        ],
        "outboundTag": "api"
      },
      {
        "outboundTag": "wg0",
        "inboundTag": [
          "<inboundTag>"
          //需要之前在 inbound 中指定好 Tag，这里是 api 生成的,还可以添加域名等等
        ]
      },
      {
        "outboundTag": "blocked",
        "protocol": [
          "bittorrent"
        ]
      }
    ]
  },
  "stats": {}
}
```

::: tip
可以通过修改 "domainStrategy": "UseIPv6"来控制对应用户的访问方式 实测优先级要高于系统本身的 gai.config
:::

## 5、系统设置配置

::: tip
需要打开系统的 ip\_forward
`sysctl -w net.ipv4.ip_forward=1`
`sysctl -w net.ipv6.conf.all.forwarding=1`
:::

## 6、完成 WireGuard 相关设置

开启隧道

```bash
wg-quick up wg0
```

开机自启

```bash
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
```

验证 IPv4/IPv6

> 在代理上 运行 `curl ip-api.com -4/-6` / 浏览器访问ip-api.com

## 后记

本文本意是可以避免的多余的流量浪费，将路由和分流的功能交给 Xray 处理。避免了维护路由表的繁琐工作。顺便技术提升 UP。

## 感谢

[XTLS/Xray-core](https://github.com/XTLS/Xray-core); [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core); [WireGuard](https://www.wireguard.com/); [@p3terx](https://p3terx.com/); @w; @Hiram; @Luminous; @Ln; @JackChou;

---

---
url: /document/level-2/warp.md
---
# 通过 Cloudflare Warp 增强代理安全性

Xray（1.6.5+）新加入了 WireGuard 出站，虽然增加的代码和依赖会增大 core 体积，但是我们认为这是一个很有必要的新功能，原因有三：

1. 通过近期的一些讨论和[实验](https://github.com/net4people/bbs/issues/129#issuecomment-1308102504)，我们知道代理回国流量是不安全的。一种应对方式是将回国流量路由至黑洞，它的缺点是由于 geosite 和 geoip 更新的不及时或者新手不知道如何在客户端适当分流，结果流量进入黑洞，影响使用体验。
   这时我们只需要将回国流量导入 Cloudflare Warp，可以在不影响使用体验的情况下达到同样的安全性。
2. 众所周知，大部分机场会记录用户访问域名的日志，某些机场还会审计和阻断一些用户流量。保护用户私密性的一个方法，就是在客户端使用链式代理。
   Warp 使用的 WireGuard 轻量级 VPN 协议会在代理层内增加一层加密。对于机场而言，用户所有流量的目标都是 Warp，从而最大程度保护自己的隐私。
3. 方便使用，只需要一个 core 即可完成分流，Wireguard Tun，链式代理的设置。

## 申请 Warp 账户

### 感谢 Cloudflare 推动自由的互联网，现在你可以免费使用 Warp 服务，连接的时候会根据出口自动选择最近的服务器

#### 方法 1：

1. 使用一台 vps，下载 [wgcf](https://github.com/ViRb3/wgcf/releases)
2. 运行 `wgcf register` 生成 `wgcf-account.toml`
3. 运行 `wgcf generate` 生成 `wgcf-profile.conf` 拷贝内容如下：

```ini
[Interface]
PrivateKey = 我的私钥
Address = 172.16.0.2/32
Address = 2606:4700:110:8949:fed8:2642:a640:c8e1/128
DNS = 1.1.1.1
MTU = 1280
[Peer]
PublicKey = Warp公钥
AllowedIPs = 0.0.0.0/0
AllowedIPs = ::/0
Endpoint = engage.cloudflareclient.com:2408
```

#### 方法 2：

1. 使用 [warp-reg.sh](https://github.com/chise0713/warp-reg.sh)，运行：

```
bash -c "$(curl -L warp-reg.vercel.app)"
```

* 输出

```json
{
  "endpoint": {
    "v4": "162.159.192.7",
    "v6": "[2606:4700:d0::a29f:c007]"
  },
  "reserved_dec": [35, 74, 190],
  "reserved_hex": "0x234abe",
  "reserved_str": "I0q+",
  "private_key": "yL0kApRiZW4VFfNkKAQ/nYxnMFT3AH0dfVkj1GAlr1k=",
  "public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
  "v4": "172.16.0.2",
  "v6": "2606:4700:110:81f3:2a5b:3cad:9d4:9ea6"
}
```

2. 拷贝输出的内容

#### 方法 3：

1. 使用[wgcf-cli](https://github.com/ArchiveNetwork/wgcf-cli)，运行以下内容进行安装：

```
bash -c "$(curl -L wgcf-cli.vercel.app)"
```

2. 运行 `wgcf-cli register` 进行注册，输出：

```json
❯ wgcf-cli register
{
    "endpoint": {
        "v4": "162.159.192.7:0",
        "v6": "[2606:4700:d0::a29f:c007]:0"
    },
    "reserved_str": "6nT5",
    "reserved_hex": "0xea74f9",
    "reserved_dec": [
        234,
        116,
        249
    ],
    "private_key": "WIAKvgUlq5fBazhttCvjhEGpu8MmGHcb1H0iHSGlU0Q=",
    "public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
    "addresses": {
        "v4": "172.16.0.2",
        "v6": "2606:4700:110:8d9c:3c4e:2190:59d1:2d3c"
    }
}
```

* 完整文件将会保存到工作目录的 `wgcf.json` 内。

3. 运行 `wgcf-cli generate --xray` 来生成一个WireGurad出站，他会将内容保存到 `wgcf.xray.json` 内

* 示例文件：

```json
{
  "protocol": "wireguard",
  "settings": {
    "secretKey": "6CRVRLgFwGajnikoVOPTDNZnDhx3EydhPsMgpxHfBCY=",
    "address": [
      "172.16.0.2/32",
      "2606:4700:110:857a:6a95:fe27:1870:2a9d/128"
    ],
    "peers": [
      {
        "publicKey": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
        "allowedIPs": ["0.0.0.0/0", "::/0"],
        "endpoint": "162.159.192.1:2408"
      }
    ],
    "reserved": [240, 25, 146],
    "mtu": 1280
  },
  "tag": "wireguard"
}
```

## 在服务端分流回国流量至 warp

在现有出站中新增一个 WireGurad 出站

```json
{
  "protocol": "wireguard",
  "settings": {
    "secretKey": "我的私钥",
    "address": [
      "172.16.0.2/32",
      "2606:4700:110:8949:fed8:2642:a640:c8e1/128"
    ],
    "peers": [
      {
        "publicKey": "Warp公钥",
        "endpoint": "engage.cloudflareclient.com:2408"
      }
    ],
    "reserved": [0, 0, 0] // 如果你有的话，粘贴reserved到这里
  },
  "tag": "wireguard-1"
}
```

路由策略推荐`IPIfNonMatch`

在现有路由中新增以下

```json
            {
                "domain": [
                    "geosite:cn"
                ],
                "outboundTag": "wireguard-1"
            },
            {
                "ip": [
                    "geoip:cn"
                ],
                "outboundTag": "wireguard-1"
            }
```

## 客户端使用 warp 链式代理

```json
{
  "outbounds": [
    {
      "protocol": "wireguard",
      "settings": {
        "secretKey": "我的私钥",
        "peers": [
          {
            "publicKey": "Warp公钥",
            "endpoint": "engage.cloudflareclient.com:2408"
          }
        ],
        "reserved": [0, 0, 0] // 如果你有的话，粘贴reserved到这里
      },
      "streamSettings": {
        "sockopt": {
          "dialerProxy": "proxy"
        }
      },
      "tag": "wireguard-1"
    },
    {
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {
        "address": "我的IP",
        "port": 我的端口,
        "id": "我的UUID",
        "security": "auto"
      },
      "streamSettings": {
        "network": "tcp"
      }
    }
  ]
}
```

---

---
url: /document/level-2/traffic_stats.md
---
# 流量统计配置教程

请熟悉[流量统计 白话文教程](https://guide.v2fly.org/advanced/traffic.html)，本文在其基础上适配了 Xray（1.5.9+）。

## 查看流量信息

配置方法与 v2fly 一致。
查看流量信息是 xray 命令行的其中一个功能。配置内设置的 api dokodemo-door 端口，即为 `--server` 参数的端口。

```bash
xray api statsquery --server=127.0.0.1:10085 #查看所有流量
xray help api statsquery #statsquery 查询匹配的记录
xray help api stats #stats 查询一个记录
```

输出例子：

```json
{
  "stat": [
    {
      "name": "inbound>>>vmess-quic>>>traffic>>>downlink",
      "value": "1176"
    },
    {
      "name": "user>>>love@example.com>>>traffic>>>downlink",
      "value": "2040"
    },
    {
      "name": "inbound>>>api>>>traffic>>>uplink",
      "value": "14247"
    },
    {
      "name": "user>>>love@example.com>>>traffic>>>uplink",
      "value": "2520"
    },
    {
      "name": "inbound>>>api>>>traffic>>>downlink",
      "value": "87618"
    },
    {
      "name": "outbound>>>direct>>>traffic>>>downlink",
      "value": "0"
    },
    {
      "name": "inbound>>>vmess-quic>>>traffic>>>uplink",
      "value": "1691"
    },
    {
      "name": "outbound>>>direct>>>traffic>>>uplink",
      "value": "0"
    }
  ]
}
```

## 流量信息的处理

把以下脚本保存到 `traffic.sh`，注意使用 `chmod 755 traffic.sh` 授予执行权限。注意调整修改 `_APISERVER` 一行的连接具体的端口参数。

```bash
#!/bin/bash

_APISERVER=127.0.0.1:10085
_XRAY=/usr/local/bin/xray

apidata () {
    local ARGS=
    if [[ $1 == "reset" ]]; then
      ARGS="-reset=true"
    fi
    $_XRAY api statsquery --server=$_APISERVER "${ARGS}" \
    | awk '{
        if (match($1, /"name":/)) {
            f=1; gsub(/^"|link"|,$/, "", $2);
            split($2, p,  ">>>");
            printf "%s:%s->%s\t", p[1],p[2],p[4];
        }
        else if (match($1, /"value":/) && f){
          f = 0;
          gsub(/"/, "", $2);
          printf "%.0f\n", $2;
        }
        else if (match($0, /}/) && f) { f = 0; print 0; }
    }'
}

print_sum() {
    local DATA="$1"
    local PREFIX="$2"
    local SORTED=$(echo "$DATA" | grep "^${PREFIX}" | sort -r)
    local SUM=$(echo "$SORTED" | awk '
        /->up/{us+=$2}
        /->down/{ds+=$2}
        END{
            printf "SUM->up:\t%.0f\nSUM->down:\t%.0f\nSUM->TOTAL:\t%.0f\n", us, ds, us+ds;
        }')
    echo -e "${SORTED}\n${SUM}" \
    | numfmt --field=2 --suffix=B --to=iec \
    | column -t
}

DATA=$(apidata $1)
echo "------------Inbound----------"
print_sum "$DATA" "inbound"
echo "-----------------------------"
echo "------------Outbound----------"
print_sum "$DATA" "outbound"
echo "-----------------------------"
echo
echo "-------------User------------"
print_sum "$DATA" "user"
echo "-----------------------------"
```

---

---
url: /document/level-2/vless_reverse.md
---
# VLESS 反向代理示例

本文演示如何使用 Xray 的 VLESS 反向代理能力，通过公网服务器把流量送回远程内网。这里给出两种常见用法：

* `入口转发`：远程端口映射，将公网入口端口映射到远程内网 Web 服务；
* `远程回家`：远程内网漫游，用户通过公网服务器中转，回到家里的内网继续访问资源。

## 入口转发

远程端口映射，把公网入口端口映射到远程内网 Web 服务。

### 工作方式

这个模型里有三个角色：

* 用户：访问公网入口；
* 公网服务器：接收流量，并将其转交给反向代理通道；
* 内网设备：主动建立到公网服务器的连接，并在反向通道中接收请求。

```mermaid
flowchart LR
    U[用户]
    S[公网服务器]
    I[内网设备]
    L[内网 Web 服务]

    I -- 主动建立 VLESS 反向连接 --> S
    U -- 访问公网 443 --> S
    S -- 通过 reverse 通道转发 --> I
    I -- 改写目标并转发 --> L
```

可以简单理解为：

1. 内网设备先主动连接到公网服务器。
2. 公网服务器保留这条反向通道。
3. 用户访问公网服务器上的 `443` 入口。
4. 公网服务器把请求通过反向通道送回内网设备。
5. 内网设备再把目标改写到实际 Web 服务。

### 配置思路

VLESS 反向代理的关键点有两个：

* 在公网侧，为某个 VLESS 客户端声明 `reverse.tag`，这样它会表现为一个可路由的出站；
* 在内网侧，为某个 VLESS 出站声明 `reverse.tag`，这样它会主动建立反向连接，并在本地表现为一个可接收流量的入口。

两端的 `reverse.tag` 不要求同名。它们只是各自配置里的本地标识，真正建立对应关系的是同一条反向连接本身。

### 公网服务器配置

下面的示例完成了两件事：

* 在 `8443` 端口提供一个 VLESS 入站，其中一个 `client` 因为带有 `reverse` 因此可以专门给内网设备建立反向连接；
* 在 `443` 端口提供一个 `tunnel` 入站，对外作为 Web 服务入口，并把这个入口收到的流量转发到反向代理通道。

同时要注意，`freedom` 出站必须保留作为占位。否则一旦 `outbounds` 为空，那么 `reverse-out` 将被视为默认出站，导致未命中规则的流量可能错误地进入反向代理通道。

```json
{
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 8443,
      "protocol": "vless",
      "settings": {
        "decryption": "mlkem768x25519plus.native.600s.aCF82eKiK6g0DIbv0_nsjbHC4RyKCc9NRjl-X9lyi0k",
        "clients": [
          {
            "id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
            "flow": "xtls-rprx-vision",
            "reverse": {
              "tag": "reverse-out"
            }
          }
          // ... 其它普通的 client
        ]
      }
    },
    {
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "tunnel",
      "tag": "portal"
    }
  ],
  "routing": {
    "rules": [
      {
        "inboundTag": ["portal"],
        "outboundTag": "reverse-out"
      }
    ]
  },
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
```

### 内网设备配置

内网设备的职责是主动连出，并建立反向通道。这里额外写一组路由，是为了把从 `reverse-in` 进入的流量明确送到指定的 `freedom` 出站，而不是完全依赖默认出站，因为通常你的内网端 Xray 还会承担日常的正向代理功能。

示例里保留了两个 `freedom` 出站：

* 一个普通 `freedom`，作为默认直连出口；
  （假设你有正向代理需求）
* 一个带 `tag` 的 `freedom`，专门用于承接从反向代理入口进来的流量。

假设你的内网 Web 服务监听在 `192.168.1.123:8888`：

* 需要在出站时把目标地址改写到这个内网地址；
* 因为 Xray 存在默认安全策略，还需要在 `finalRules` 里显式放行目标端口。

```json
{
  // 关于正向代理的其它配置略...
  "routing": {
    "rules": [
      {
        "inboundTag": ["reverse-in"],
        "outboundTag": "reverse-direct"
      }
    ]
  },
  "outbounds": [
    {
      "protocol": "freedom"
    },
    {
      "protocol": "freedom",
      "tag": "reverse-direct",
      "settings": {
        "redirect": "192.168.1.123:8888",
        "finalRules": [
          {
            "action": "allow",
            "network": "tcp",
            "ip": "192.168.1.123",
            "port": "8888"
          }
        ]
      }
    },
    {
      "protocol": "vless",
      "settings": {
        "address": "yourserver.com",
        "port": 8443,
        "encryption": "mlkem768x25519plus.native.0rtt.2PcBa3Yz0zBdt4p8-PkJMzx9hIj2Ve-UmrnmZRPnpRk",
        "id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
        "flow": "xtls-rprx-vision",
        "reverse": {
          "tag": "reverse-in"
        }
      }
    }
  ]
}
```

需要注意的是：

* 公网侧的 `reverse.tag` 会表现为一个出站；
* 内网侧的 `reverse.tag` 会表现为一个入口；
* 它们不必同名，只需要通过同一条反向连接 `ac04551d...` 对应起来；
* 如果你希望内网侧对反向代理进来的流量做更细的控制，可以像上面的示例一样，在 `routing` 中显式指定它该走哪个 `freedom` 出站。
* 内网侧还支持 `sniffing` 并且如果你在 freedom 配置了 `proxyProtocol` 甚至可以让 WebServer 看到真实的访客 IP 这里不做展开。

### 请求流向

```mermaid
sequenceDiagram
    participant U as 用户
    participant S as 公网服务器
    participant I as 内网设备
    participant L as 内网 Web 服务

    I->>S: 建立 VLESS 反向连接
    U->>S: 访问公网 443 端口
    S->>S: portal 入站命中路由
    S->>I: 转发到 reverse-out
    I->>L: 改写到 192.168.1.123:8888
    L-->>I: 返回响应
    I-->>S: 经反向通道返回
    S-->>U: 响应用户
```

这种用法的语义很明确：用户感知到的是“访问公网服务器上的 Web 服务”，而实际处理请求的是远程内网设备上的 Web 服务。

### 多线路与冗余

一个内网设备可以建立多条反向连接，例如分别通过不同网络、不同出口，或者不同入口地址连接到公网服务器。只要公网侧把这些连接都视为同一个反向代理目标，就可以形成冗余线路。

这种方式的好处是：

* 某条线路暂时不可用时，流量仍可走其他线路；
* 公网侧无需为每条线路单独设计一套路由逻辑；
* 对内网穿透场景来说，更方便做链路冗余。

### 安全建议

* 公网服务器务必保留明确的默认出站，根据你的实际需求可以是 `freedom` 或 `blackhole` 等，避免未命中路由的流量误入反向代理；
* 示例配置偏重说明原理，真实公网环境中通常还需要结合更完整的传输与伪装方案。

## 远程回家

远程内网漫游，用户通过公网服务器中转，回到家里的内网继续访问资源。

### 场景说明

这一部分不是把某个公网端口暴露给外部访问，而是让用户先连接公网服务器上的 VLESS，再借助已经建立好的反向通道，把这个用户的流量继续送回家里的内网设备处理。

这种用法更接近：

* 在外漫游时访问家里的局域网资源；
* 把特定用户的出口放回家里；
* 通过公网服务器中转，回到家庭网络继续访问 NAS、路由器面板、家庭 DNS 或其他内网服务。

```mermaid
flowchart LR
    U[外部用户设备]
    S[公网服务器]
    I[家中设备]
    H[家庭局域网资源]

    I -- 主动建立 VLESS 反向连接 --> S
    U -- 连接公网服务器上的 VLESS 入口 --> S
    S -- 用户流量经 reverse 转发 --> I
    I -- 再访问家庭局域网 --> H
```

### 配置思路

和第一篇相比，这里最大的区别不在于反向连接本身，而在于公网侧的路由目标：

* 第一篇是 `inboundTag -> reverse-out`，把某个入口端口映射回内网；
* 第二篇是 `user -> reverse-out`，把某个用户的代理流量交给内网设备继续处理。

也就是说，公网服务器在这里更像一个中转站。用户并不是直接“访问公网端口映射到的服务”，而是“先接入公网服务器上的 VLESS 入口，再从家里的设备继续出发”。

### 公网服务器配置

这个示例里：

* 第一个 UUID 仍然用于家中设备建立反向连接；
* 第二个 UUID 用于外部用户接入公网服务器；
* 路由根据 `email` 把该用户的流量转发到 `reverse-out`。

```json
{
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 8443,
      "protocol": "vless",
      "settings": {
        "decryption": "mlkem768x25519plus.native.600s.aCF82eKiK6g0DIbv0_nsjbHC4RyKCc9NRjl-X9lyi0k",
        "clients": [
          {
            "id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
            "flow": "xtls-rprx-vision",
            "reverse": {
              "tag": "reverse-out"
            }
          },
          {
            "id": "e8758aff-d830-4d08-a59e-271df65b995a",
            "flow": "xtls-rprx-vision",
            "email": "roam@example.com"
          }
        ]
      }
    }
  ],
  "routing": {
    "rules": [
      {
        "user": ["roam@example.com"],
        "outboundTag": "reverse-out"
      }
    ]
  },
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
```

### 家中设备配置

家中设备依然要主动连接公网服务器建立反向通道。和第一篇不同，这次它不是把流量统一改写到一个固定 Web 服务，而是把来自 `reverse-in` 的流量统一交给家里的直连出口处理。

下面这个示例假设：

* 家庭局域网网段为 `192.168.1.0/24`；
* 用户设备侧已经负责决定哪些流量需要“回家”；
* 家中设备这一侧只负责承接这些流量，并交给家庭网络继续处理。

```json
{
  "routing": {
    "rules": [
      {
        "inboundTag": ["reverse-in"],
        "outboundTag": "home-direct"
      }
    ]
  },
  "outbounds": [
    {
      "protocol": "freedom"
    },
    {
      "protocol": "freedom",
      "tag": "home-direct",
      "settings": {
        "finalRules": [
          {
            "action": "allow",
            "network": "tcp,udp",
            "ip": ["192.168.1.0/24"]
          }
        ]
      }
    },
    {
      "protocol": "vless",
      "settings": {
        "address": "yourserver.com",
        "port": 8443,
        "encryption": "mlkem768x25519plus.native.0rtt.2PcBa3Yz0zBdt4p8-PkJMzx9hIj2Ve-UmrnmZRPnpRk",
        "id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
        "flow": "xtls-rprx-vision",
        "reverse": {
          "tag": "reverse-in"
        }
      }
    }
  ]
}
```

上面这组规则的含义是：

* 只要流量是从 `reverse-in` 进入的，就转给 `home-direct`；
* 因为 Xray 存在默认安全策略，需要用 `finalRules` 显式放通家里内网。如果你只是想访问家里 NAS 那么建议你不要像示例配置那样放通全部 IP，而是按实际需要放通指定 IP 和 Port。

### 用户设备配置

用户设备这一侧不建议默认“全量回家”，更常见的做法是只把访问家庭资源的流量拨回去，其余流量仍然本地直连。下面给出一套和上面家中设备配置对应的示例。假设：

* 用户只想访问家里的 `192.168.1.0/24` 网段；
* 其他流量继续本地直连。

```json
{
  "routing": {
    "rules": [
      {
        "ip": ["192.168.1.0/24"],
        "outboundTag": "roam-home"
      }
    ]
  },
  "outbounds": [
    {
      "protocol": "freedom"
    },
    {
      "protocol": "vless",
      "tag": "roam-home",
      "settings": {
        "address": "yourserver.com",
        "port": 8443,
        "encryption": "mlkem768x25519plus.native.0rtt.2PcBa3Yz0zBdt4p8-PkJMzx9hIj2Ve-UmrnmZRPnpRk",
        "id": "e8758aff-d830-4d08-a59e-271df65b995a",
        "flow": "xtls-rprx-vision"
      }
    }
  ]
}
```

上面这组规则的含义是：

* 发往 `192.168.1.0/24` 的流量走 `roam-home`；
* 其他流量因为没有命中规则，继续走默认的 `freedom`，也就是本地直连。

这样用户看起来就像“只把访问家里资源的那部分流量拨回家”，而不是把所有上网流量都先绕到公网服务器再回家中转。

### 请求流向

```mermaid
sequenceDiagram
    participant U as 外部用户设备
    participant S as 公网服务器
    participant I as 家中设备
    participant H as 家庭局域网资源

    I->>S: 建立 VLESS 反向连接
    U->>S: 使用 roam@example.com 接入公网服务器
    S->>S: user 路由命中 reverse-out
    S->>I: 将用户流量送入 reverse 通道
    I->>H: 访问 NAS / 路由器 / 家庭 DNS / 内网服务
    H-->>I: 返回响应
    I-->>S: 经反向通道返回
    S-->>U: 响应用户
```

### 这种用法和第一篇的区别

* 第一篇是“把公网一个入口端口映射到远程内网某个固定服务”，无需额外身份验证，全世界谁都可以访问此服务；
* 第二篇是“让某个用户先拨入公网的 Xray 服务器，再借道反向代理回家继续访问”；
* 第一篇更像远程端口映射，也称内网穿透；
* 第二篇更像远程漫游或轻量异地组网。

### 安全建议

* 如果有多个漫游用户，建议每个用户使用独立 UUID 或独立标识；
* 示例配置为追求简化仅用了 VLESS-enc 实际使用时可能需要使用 REALITY/XHTTP 等伪装流量。

## 小结

VLESS 反向代理至少可以覆盖两类场景：

* 把公网入口端口映射到远程内网固定服务；
* 让用户接入公网服务器后再通过反向通道漫游回家。

两者使用的是同一套反向连接机制，区别主要在公网侧如何路由流量，以及内网侧如何继续处理这些流量。理解这一点之后，就可以按自己的场景在“端口映射”和“远程漫游”之间自由扩展。

---

---
url: /development.md
---
# 开发指南

## 编译文档

Xray 支持各种平台, 您可以在多种平台上自行进行交叉编译。

请点击[编译文档](./intro/compile.md)以查看具体编译相关内容。

## 设计思路

Xray 内核提供了一个平台，在其之上可以进二次开发。

这个章节阐述了 Xray 的设计目标和架构。

请点击[设计思路](./intro/design.md)以了解 Xray 的设计目标和架构。

## 开发规范

这个章节阐述了获取代码,进行开发,提交 PR 的流程中需要遵循的准则, 以及相关的编码规范。

请点击[开发规范](./intro/guide.md)查看 Xray 开发中应遵循的准则。

## 协议详解

Xray 用到了很多种协议, 您可以通过各种途径获得协议的详细描述。

### [VLESS 协议](./protocols/vless.md)

VLESS 是一个无状态的轻量传输协议，可以作为 Xray 客户端和服务器之间的桥梁。

### [VMess 协议](./protocols/vmess.md)

VMess 是一个加密传输协议，可以作为 Xray 客户端和服务器之间的桥梁。

### [Mux.Cool 协议](./protocols/muxcool.md)

Mux.Cool 协议是一个多路复用传输协议，用于在一条已建立的数据流中传输多个各自独立的数据流。

### [mKCP 协议](./protocols/mkcp.md)

mKCP 是流式传输协议，由 [KCP 协议](https://github.com/skywind3000/kcp)修改而来，可以按顺序传输任意的数据流。

---

---
url: /development/intro/compile.md
---
# 编译文档

## 前序工作

Xray 使用 [Golang](https://golang.org/) 作为编程语言，你需要先安装最新版本 Golang 才能够编译。

::: tip TIP
安装 Golang: [golang.org/doc/install](https://golang.org/doc/install)
:::

> 如果你不幸使用 Windows, 请 **务必** 使用 Powershell

## 拉取 Xray 源代码

```bash
git clone https://github.com/XTLS/Xray-core.git
cd Xray-core && go mod download
```

> 如果你闲的没事干，可以试试 GitHub 官方工具: `gh repo clone XTLS/Xray-core`

注意：在无法正常访问 Google 的网络环境，依赖无法被正常拉取，需要先设置 `GOPROXY`：

```bash
go env -w GOPROXY=https://goproxy.io,direct
```

## 构建二进制

:::warning
本小节的命令需要在 Xray 根目录内运行。
:::

### Windows(Powershell):

```powershell
$env:CGO_ENABLED=0
go build -o xray.exe -trimpath -buildvcs=false -ldflags "-s -w -buildid=" ./main
```

### macOS, Linux:

```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags "-s -w -buildid=" ./main
```

运行以上命令会在目录下生成 xray 可执行文件。

::: tip
如果需要编译可以进行 debug 的程序,即可以用 dlv 附加到运行的程序进行调试, 请去掉 ldflags 中的 '-w -s' 选项.

-w 禁止生成 debug 信息。使用该选项后，将无法使用 gdb 进行调试。
-s 禁用符号表
PS:其实用 vscode 或其他 IDE 调试似乎更方便。
:::

## 交叉编译：

这里以在 Windows(Powershell) 环境中，编译到 Linux 服务器为例：

```powershell
$env:CGO_ENABLED=0
$env:GOOS="linux"
$env:GOARCH="amd64"

go build -o xray -trimpath -buildvcs=false -ldflags "-s -w -buildid=" ./main
```

上传到服务器后，记得在服务器终端内执行 `chmod +x xray`

::: tip
执行 `go tool dist list` 查看所有支持的系统与架构。
:::

## 可复现构建：

使用以下命令进行构建（`<short commit ID>` 应替换为对应的提交 SHA-256 前七位）：

```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=<short commit ID> -s -w -buildid=" -v ./main
```

其中对 MIPS/MIPSLE 架构，应该使用：

```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=<short commit ID> -s -w -buildid=" -v ./main
```

::: warning
请先确认您使用的 Golang 版本与编译 Release 的一致。
:::

## 编译用于 Windows 7 的版本

将 Golang 工具替换为 [go-win7](https://github.com/XTLS/go-win7) 中提供的版本，再安装上述步骤进行编译。

---

---
url: /development/intro/design.md
---
# 设计目标

* Xray 内核提供了一个平台，支持必要的网络代理功能，在其之上可以进二次开发，以提供更好的用户体验；
* 以跨平台为首要原则，以减少二次开发的成本；

## 架构

![Architecture](./framework.png)

内核分为三层：应用层、代理层和传输层。

每一层内包含数个模块，模块间互相独立，同类型的模块可无缝替换。

### 应用层

应用层包含一些代理层中常用的功能，这些功能被抽象出来，以便在不同的代理模块中复用。

应用层的模块应为纯软件实现，与硬件或平台相关的技术无关。

重要模块列表：

* Dispatcher: 用于把入站代理所接收到的数据，传送给出站代理；
* Router: 路由模块，详见 [路由配置](../../config/routing.md)；
* DNS: 内置的 DNS 服务器模块；
* Proxy Manager: 代理管理器；

### 代理层

代理层分为两部分：入站代理（Inbound Proxy）和出站代理（Outbound Proxy）。

两部分相互独立，入站代理不依赖于某个特定的出站代理，反之亦然。

#### 入站代理

* 实现 [proxy.Inbound](https://github.com/xtls/Xray-core/blob/main/proxy/proxy.go) 接口；

#### 出站代理

* 实现 [proxy.Outbound](https://github.com/xtls/Xray-core/blob/main/proxy/proxy.go) 接口；

### 传输层

传输层提供一些网络数据传输相关的工具模块。

---

---
url: /development/intro/guide.md
---
# 开发规范

## 基本

### 版本控制

Project X 的代码被托管在 github 上:

* Xray 核心 [Xray-core](https://github.com/XTLS/Xray-core)
* 安装脚本 [Xray-install](https://github.com/XTLS/Xray-install)
* 配置模板 [Xray-examples](https://github.com/XTLS/Xray-examples)
* Xray 文档 [Xray-docs-next](https://github.com/XTLS/Xray-docs-next)

您可以使用 [Git](https://git-scm.com/) 来获取代码。

### 分支（Branch）

* 本项目的主干分支为 main，
* 本项目的发布主分支同为 main，
* 需要确保 main 在任一时刻都是可编译，且可正常使用的。
* 如果需要开发新的功能，请新建分支进行开发，在开发完成并且经过充分测试后，合并回主干分支。
* 已经合并入主干且没有必要存在的分支，请删除。

### 发布（Release）

* 建立尝鲜版本和稳定版本两个发布通道
  * 尝鲜版本，可以为 daily build，主要用于特定情况的测试，尝鲜和获得即时反馈和再改进。
  * 稳定版本，为定时更新(比如月更)，合并稳定的修改并发布。

### 引用其它项目

* Golang
  * 产品代码建议使用 Golang 标准库和 [golang.org/x/](https://pkg.go.dev/search?limit=25\&m=package\&q=golang.org%2Fx) 下的库；
  * 如需引用其它项目，请事先创建 issue 讨论；
* 其它
  * 不违反双方的协议，且对项目有帮助的工具，都可以使用。

## 开发流程

### 写代码之前

发现任何问题，或对项目有任何想法，请创建 [issue](https://github.com/XTLS/Xray-core/issues) 讨论以减少重复劳动和消耗在代码上的时间。

### 修改代码

* Golang
  * 请参考 [Effective Go](https://golang.org/doc/effective_go.html)；
  * 每一次 push 之前，请运行：`go generate core/format.go`；
  * 如果需要修改 protobuf，例如增加新配置项，请运行：`go generate core/proto.go`；
  * 提交 pull request 之前，建议测试通过：`go test ./...`；
  * 提交 pull request 之前，建议新增代码有超过 70% 的代码覆盖率（code coverage）；
* 其它
  * 请注意代码的可读性。

### Pull Request

* 提交 PR 之前，请先运行 `git pull https://github.com/XTLS/Xray-core.git` 以确保 merge 可顺利进行；
* 一个 PR 只做一件事，如有对多个 bug 的修复，请对每一个 bug 提交一个 PR；
* 由于 Golang 的特殊需求（Package path），Go 项目的 PR 流程和其它项目有所不同，建议流程如下：
  1. 先 Fork 本项目，创建你自己的 `github.com/<your_name>/Xray-core.git` 仓库；
  2. 克隆你自己的 Xray 仓库到本地：`git clone https://github.com/<your_name>/Xray-core.git`；
  3. 基于 `main` 分支创建新的分支，例如 `git branch issue24 main`；
  4. 在新创建的分支上作修改并提交修改(commit)；
  5. 在推送(push)修改完成的分支到自己的仓库前，先切换到 `main` 分支，运行 `git pull https://github.com/XTLS/Xray-core.git` 拉取最新的远端代码；
  6. 如果上一步拉取得到了新的远端代码，则切换到之前自己创建的分支，运行 `git rebase main` 执行分支合并操作。如遇到文件冲突，则需要解决冲突；
  7. 上一步处理完毕后，就可以把自己创建的分支推送到自己的仓库：`git push -u origin your-branch`
  8. 最后，把自己仓库的新推送的分支往 `XTLS/Xray-core` 的 `main` 分支发 PR 即可；
  9. 请在 PR 的标题和正文中，完整表述此次 PR 解决的问题 / 新增的功能 / 代码所做的修改的用意等；
  10. 耐心等待开发者的回应。

### 对代码的修改

#### 功能性问题

请提交至少一个测试用例（Test Case）来验证对现有功能的改动。

#### 性能相关

请提交必要的测试数据来证明现有代码的性能缺陷，或是新增代码的性能提升。

#### 新功能

* 如果新增功能对已有功能不影响，请提供可以开启/关闭的开关（如 flag），并使新功能保持默认关闭的状态；
* 大型新功能（比如增加一个新的协议）开发之前，请先提交一个 issue，讨论完毕之后再进行开发。

#### 其它

视具体情况而定。

## Xray 编码规范

以下内容适用于 Xray 中的 Golang 代码。

### 代码结构

```
Xray-core
├── app        // 应用模块
│   ├── router // 路由
├── common     // 公用代码
├── proxy      // 通讯协议
│   ├── blackhole
│   ├── dokodemo-door
│   ├── freedom
│   ├── socks
│   ├── vmess
├── transport  // 传输模块
```

### 编码规范

基本与 Golang 官方所推荐做法一致，有一些例外。写在这里以方便大家熟悉 Golang。

#### 命名

* 文件和目录名尽量使用单个英文单词，比如 hello.go；
  * 如果实在没办法，则目录使用连接线／文件名使用下划线连接两个（或多个单词），比如 hello-world/hello\_again.go；
  * 测试代码使用 \_test.go 结尾；
* 类型使用 Pascal 命名法，比如 ConnectionHandler；
  * 对缩写不强制小写，即 HTML 不必写成 Html；
* 公开成员变量也使用 Pascal 命名法；
* 私有成员变量使用 [小驼峰式命名法](https://zh.wikipedia.org/wiki/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB) ，如 `privateAttribute` ；
* 为了方便重构，方法建议全部使用 Pascal 命名法；
  * 完全私有的类型放入 `internal` 。

#### 内容组织

* 一个文件包含一个主要类型，及其相关的私有函数等；
* 测试相关的文件，如 Mock 等工具类，放入 testing 子目录。

#### Int32Range

For end user

一个表示可选范围的值，可以是以下几种写法

-包含在引号里的单独数字或范围

* `""` (视为0) 注意部分字段完全不设置和设置为空可能是两种概念
* `"114"`
* `"114-514"`

-独立的int，这种情况下只能是单数字

* `114`

For dev

如果需要在配置文件中包含范围，请使用 `Int32Range` 类型

使用 .From 和 .To 获取值，在 From > To 比如 1919-810 时会自动交换数值确保 From 会小于 To, 如果要获取原始值可以用 .Left 和 .Right

---

---
url: /development/protocols/vless.md
---
# VLESS 协议

VLESS 是一个无状态的轻量传输协议，可以作为 Xray 客户端和服务器之间的桥梁。

## Request & Response

| 1 字节   | 16 字节   | 1 字节         | M 字节            | 1 字节 | 2 字节 | 1 字节   | S 字节 | X 字节   |
| -------- | --------- | -------------- | ----------------- | ------ | ------ | -------- | ------ | -------- |
| 协议版本 | 等价 UUID | 附加信息长度 M | 附加信息 ProtoBuf | 指令   | 端口   | 地址类型 | 地址   | 请求数据 |

| 1 字节                 | 1 字节         | N 字节            | Y 字节   |
| ---------------------- | -------------- | ----------------- | -------- |
| 协议版本，与请求的一致 | 附加信息长度 N | 附加信息 ProtoBuf | 响应数据 |

VLESS 早在第二个测试版 ALPHA 2 时就已经是上述结构了（BETA 是第五个测试版）：

> “响应认证”被替换为“协议版本”并移至最前，使 VLESS 可以升级换代，同时消除了生成伪随机数的开销。混淆相关结构被替换为附加信息（ProtoBuf）并前移，赋予协议本身可扩展性，相关开销也极小（[gogo/protobuf](https://github.com/gogo/protobuf)），若无附加信息则无相关开销。

我一直觉得“响应认证”不是必要的，ALPHA 时为了提升生成随机数的性能，还用 math/rand 替换 crypto/rand，而现在都不需要了。

“协议版本”不仅能起到“响应认证”的作用，还赋予了 VLESS 无痛升级协议结构的能力，带来无限的可能性。
“协议版本”在测试版本中均为 0，正式版本中为 1，以后若有不兼容的协议结构变更则应升级版本。

VLESS 服务端的设计是 switch version，即同时支持所有 VLESS 版本。若需要升级协议版本（可能到不了这一步），推荐的做法是服务端提前一个月支持，一个月后再改客户端。VMess 请求也有协议版本，但它的认证信息在外面，指令部分则高度耦合且有固定加密，导致里面的协议版本毫无意义，服务端也没有进行判断，响应则没有协议版本。Trojan 的协议结构中没有协议版本。

接下来是 UUID，我本来觉得 16 字节有点长，曾经考虑过缩短它，但后来看到 Trojan 用了 56 个可打印字符（56 字节），就彻底打消了这个念头。服务端每次都要验证 UUID，所以性能也很重要：VLESS 的 Validator 经历了多次重构/升级，相较于 VMess，它十分简洁且耗资源很少，可以同时支持非常多的用户，性能也十分强悍，验证速度极快（sync.Map）。API 动态增删用户则更高效顺滑。
https://github.com/XTLS/Xray-core/issues/158

引入 ProtoBuf 是一个创举，等下会详细讲解。“指令”到“地址”的结构目前与 VMess 完全相同，同样支持 Mux。

总体上，ALPHA 2 到 BETA 主要是：结构进化、清理整合、性能提升、更加完善。这些都是一点一滴的，详见 [VLESS Changes](https://github.com/rprx/v2ray-vless/releases)。

## ProtoBuf

似乎只有 VLESS 可选内嵌 ProtoBuf，它是一种数据交换格式，信息被紧密编码成二进制，TLV 结构（Tag Length Value）。

起因是我看到一篇文章称 SS 有一些缺点，如没有设计错误回报机制，客户端没办法根据不同的错误采取进一步的动作。
（但我并不认同所有错误都要回报，不然防不了主动探测。下一个测试版中，服务器可以返回一串自定义信息。）
于是想到一个可扩展的结构是很重要的，未来它也可以承载如动态端口指令。不止响应，请求也需要类似的结构。
本来打算自己设计 TLV，接着发觉 ProtoBuf 就是此结构、现成的轮子，完全适合用来做这件事，各语言支持等也不错。

目前“附加信息”只有 Scheduler 和 SchedulerV，它们是 MessName 和 MessSeed 的替代者，**当你不需要它们时，“附加信息长度”为 0，也就不会有 ProtoBuf 序列化/反序列化的开销**。其实我更愿意称这个过程为“拼接”，因为 pb 实际原理上也只是这么做而已，相关开销极小。拼接后的 bytes 十分紧凑，和 ALPHA 的方案相差无几，有兴趣的可以分别输出并对比。

为了指示对附加信息（Addons，也可以理解成插件，以后可以有很多个插件）的不同支持程度，下个测试版会在“附加信息长度”前新增“附加信息版本”。256 - 1 = 255 字节是够用且合理的（65535 就太多了，还可能有人恶意填充），现有的只用了十分之一，以后也不会同时有那么多附加信息，且大多数情况下是完全没有附加信息的。真不够用的话，可以升级 VLESS 版本。

为了减少逻辑判断等开销，暂定 Addons 不使用多级结构。一个月前出现过“可变协议格式”的想法，pb 是可以做到打乱顺序，但没必要，因为现代加密的设计不会让旁观者看出两次传输的头部相同。

下面介绍 Schedulers 和 Encryption 的构想，**它们都是可选的**，一个应对流量时序特征问题，一个应对密码学上的问题。

## ~~Schedulers~~ Flow

\~~中文名暂称：流量调度器~~（2020-09-03 更新：中文名确定为“流控”），指令由 ProtoBuf 承载，控制的是数据部分。

我之前发现，VMess 原有的 shake “元数据混淆”在 TLS 上完全不会带来有意义的改变，只会降低性能，所以 VLESS 弃用了它。并且，“混淆”这个表述容易被误解成伪装，也弃用了。顺便一提，我一直是不看好伪装的：做不到完全一样，那不就是强特征吗？做得到完全一样，那为什么不直接用伪装目标？我一开始用的是 SSR，后来发现它只是表面伪装骗运营商，就再也没用过了。

那么，“流量调度器”要解决什么问题？它影响的是宏观流量时序特征，而不是微观特征，后者是加密要解决的事情。流量时序特征可以是协议带来的，比如 Socks5 over TLS 时的 Socks5 握手 ，TLS 上不同的这种特征对于监测者来说就是不同的协议，此时无限 Schedulers 就相当于无限协议（重新分配每次发送的数据量大小等）。流量时序特征也可以是行为带来的，比如访问 Google 首页时加载了多少文件、顺序、每个文件的大小，多套一层加密并不能有效掩盖这些信息。

Schedulers 没必要像下面的 Encryption 一样整个套在外面，因为头部的一丁点数据相对于后面的数据量来说太微不足道了。

BETA 2 预计推出两个初级的 Scheduler：Zstd 压缩、数据量动态扩充。进阶操作才是从宏观层面来控制、分配，暂时咕咕。

## Encryption

与 VMess 的高度耦合不同，VLESS 的服务端、客户端不久后可以提前约定好加密方式，仅在外面套一层加密。这有点类似于使用 TLS，不影响承载的任何数据，也可以理解成底层就是从 TLS 换成预设约定加密。相对于高度耦合，这种方式更合理且灵活：一种加密方式出了安全性问题，直接扔掉并换用其它的就行了，十分方便。VLESS 服务端还会允许不同的加密方式共存。

对比 VMess，VLESS 相当于把 security 换成 encryption，把 disableInsecureEncryption 换成 decryption，就解决了所有问题。目前 encryption 和 decryption 只接受 "none" 且不能留空（即使以后有连接安全性检查），详见 [VLESS 配置文档](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md)。encryption 并不需要往外移一级，一是因为无法复用很多代码，二是因为会影响控制粒度，看未来的应用就明白了。

加密支持两类形式，一类是加密完全独立，需要额外密码，适合私用，另一类是结合已有的 UUID 来加密，适合公用。
（若用第一类加密形式，且密码是以某种形式公开的，比如多人共用，那么中间人攻击就不远了）
重新设计的动态端口可能会随加密同时推出，指令由 ProtoBuf 承载，具体实现和 VMess 的动态端口也会有很多不同。

套现成加密是件很简单的事情，也就多一层 writer & reader。BETA 3 预计支持 SS 的 aes-128-gcm 和 chacha20-ietf-poly1305：
客户端的 encryption 可以填 “auto: ss\_aes-128-gcm\_0\_123456, ss\_chacha20-ietf-poly1305\_0\_987654”，auto 会选择最适合当前机器的，0 代表测试版，最后的是密码。服务端的 decryption 也是类似填法，收到请求时会逐一尝试解密。

并不是所有组合都需逐一尝试：VMess 的加密分为三段，第一段是认证信息，结合了 UUID、alterId、时间因素，第二段是指令部分，以固定算法加密，指令中含有数据部分使用的加密算法，第三段才是重要的数据部分。可以看出，VMess 的加解密方式实际上是多对一（服务端适配），而不仅是结合 UUID。但仅是结合 UUID 来加密也是件相对麻烦的事情，短时间内不会出，鉴于我们现在有 VMessAEAD 可用，也并不着急。若 VLESS 推出了结合 UUID 的加密方式，相当于重构了整个 VMess。

## UDP issues

[XUDP：VLESS & VMess & Mux UDP FullCone NAT](https://github.com/XTLS/Xray-core/discussions/252)

## 客户端开发指引

1. VLESS 协议本身还会有不兼容升级，但客户端配置文件参数基本上是只增不减的。iOS 客户端的协议实现则需紧跟升级。
2. **视觉标准：UI 标识请统一用 VLESS**，而不是 VLess / Vless / vless，配置文件不受影响，代码内则顺其自然。
3. `encryption` 应做成输入框而不是选择框，新配置的默认值应为 `none`，若用户置空则应代填 `none`。

## VLESS 分享链接标准

感谢  [@DuckSoft](https://github.com/DuckSoft) 的提案!

详情请见 [VMessAEAD / VLESS 分享链接标准提案](https://github.com/XTLS/Xray-core/issues/91)

---

---
url: /development/protocols/vmess.md
---
# VMess 协议

VMess 是一个加密传输协议，可以作为 Xray 客户端和服务器之间的桥梁。

## 版本

当前版本号为 1。

## 依赖

### 底层协议

VMess 是一个基于 TCP 的协议，所有数据使用 TCP 传输。

### 用户 ID

ID 等价于 [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)，是一个 16 字节长的随机数，它的作用相当于一个令牌（Token）。
一个 ID 形如：de305d54-75b4-431b-adb2-eb6b9e546014，几乎完全随机，可以使用任何的 UUID 生成器来生成，比如[这个](https://www.uuidgenerator.net/)。

用户 ID 可在[配置文件](../../config/)中指定。

### 函数

* MD5: [MD5 函数](https://en.wikipedia.org/wiki/MD5)
  * 输入参数为任意长度的 byte 数组
  * 输出为一个 16 byte 的数组
* HMAC: [HMAC 函数](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
  * 输入参数为：
    * H：散列函数
    * K：密钥，任意长度的 byte 数组
    * M：消息，任意长度的 byte 数组
* Shake: [SHA3-Shake128 函数](https://en.wikipedia.org/wiki/SHA-3)
  * 输入参数为任意长度的字符串
  * 输出为任意长度的字符串

## 通讯过程

VMess 是一个无状态协议，即客户端和服务器之间不需要握手即可直接传输数据，每一次数据传输对之前和之后的其它数据传输没有影响。

VMess 的客户端发起一次请求，服务器判断该请求是否来自一个合法的客户端。如验证通过，则转发该请求，并把获得的响应发回给客户端。

VMess 使用非对称格式，即客户端发出的请求和服务器端的响应使用了不同的格式。

## 客户端请求

| 16 字节  | X 字节   | 余下部分 |
| -------- | -------- | -------- |
| 认证信息 | 指令部分 | 数据部分 |

### 认证信息

认证信息是一个 16 字节的哈希（hash）值，它的计算方式如下：

* H = MD5
* K = 用户 ID (16 字节)
* M = UTC 时间，精确到秒，取值为当前时间的前后 30 秒随机值(8 字节, Big Endian)
* Hash = HMAC(H, K, M)

### 指令部分

指令部分经过 AES-128-CFB 加密：

* Key：MD5(用户 ID + \[]byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
* IV：MD5(X + X + X + X)，X = \[]byte(认证信息生成的时间) (8 字节, Big Endian)

|   1 字节   |   16 字节   |   16 字节    |   1 字节   |  1 字节  |  4 位  |     4 位     | 1 字节 |  1 字节  |  2 字节   |   1 字节   | N 字节 | P 字节 | 4 字节 |
| :--------: | :---------: | :----------: | :--------: | :------: | :----: | :----------: | :----: | :------: | :-------: | :--------: | :----: | :----: | :----: |
| 版本号 Ver | 数据加密 IV | 数据加密 Key | 响应认证 V | 选项 Opt | 余量 P | 加密方式 Sec |  保留  | 指令 Cmd | 端口 Port | 地址类型 T | 地址 A | 随机值 | 校验 F |

选项 Opt 细节：（当某一位为 1 时，表示该选项启用）

|  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|  X  |  X  |  X  |  X  |  X  |  M  |  R  |  S  |

其中：

* 版本号 Ver：始终为 1；
* 数据加密 IV：随机值；
* 数据加密 Key：随机值；
* 响应认证 V：随机值；
* 选项 Opt：
  * S (0x01)：标准格式的数据流（建议开启）；
  * R (0x02)：客户端期待重用 TCP 连接（Xray 2.23+ 弃用）；
    * 只有当 S 开启时，这一项才有效；
  * M (0x04)：开启元数据混淆（建议开启）；
    * 只有当 S 开启时，这一项才有效；
    * 当其项开启时，客户端和服务器端需要分别构造两个 Shake 实例，分别为 RequestMask = Shake(请求数据 IV), ResponseMask = Shake(响应数据 IV)。
  * X：保留
* 余量 P：在校验值之前加入 P 字节的随机值；
* 加密方式：指定数据部分的加密方式，可选的值有：
  * 0x00：AES-128-CFB；
  * 0x01：不加密；
  * 0x02：AES-128-GCM；
  * 0x03：ChaCha20-Poly1305；
* 指令 Cmd：
  * 0x01：TCP 数据；
  * 0x02：UDP 数据；
* 端口 Port：Big Endian 格式的整型端口号；
* 地址类型 T：
  * 0x01：IPv4
  * 0x02：域名
  * 0x03：IPv6
* 地址 A：
  * 当 T = 0x01 时，A 为 4 字节 IPv4 地址；
  * 当 T = 0x02 时，A 为 1 字节长度（L） + L 字节域名；
  * 当 T = 0x03 时，A 为 16 字节 IPv6 地址；
* 校验 F：指令部分除 F 外所有内容的 FNV1a hash；

### 数据部分

当 Opt(S) 开启时，数据部分使用此格式。实际的请求数据被分割为若干个小块，每个小块的格式如下。服务器校验完所有的小块之后，再按基本格式的方式进行转发。

| 2 字节 | L 字节 |
| :----: | :----: |
| 长度 L | 数据包 |

其中：

* 长度 L：Big Endian 格式的整型，最大值为 2^14；
  * 当 Opt(M) 开启时，L 的值 = 真实值 xor Mask。Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte()；
* 数据包：由指定的加密方式加密过的数据包；

在传输结束之前，数据包中必须有实际数据，即除了长度和认证数据之外的数据。当传输结束时，客户端必须发送一个空的数据包，即 L = 0（不加密） 或认证数据长度（有加密），来表示传输结束。

按加密方式不同，数据包的格式如下：

* 不加密：
  * L 字节：实际数据；
* AES-128-CFB：整个数据部分使用 AES-128-CFB 加密
  * 4 字节：实际数据的 FNV1a hash；
  * L - 4 字节：实际数据；
* AES-128-GCM：Key 为指令部分的 Key，IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增，每个数据包加 1；IV 为 指令部分 IV 的第 3 至第 12 字节。
  * L - 16 字节：实际数据；
  * 16 字节：GCM 认证信息
* ChaCha20-Poly1305：Key = MD5(指令部分 Key) + MD5(MD5(指令部分 Key))，IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增，每个数据包加 1；IV 为 指令部分 IV 的第 3 至第 12 字节。
  * L - 16 字节：实际数据；
  * 16 字节：Poly1305 认证信息

## 服务器应答

应答头部数据使用 AES-128-CFB 加密，IV 为 MD5(数据加密 IV)，Key 为 MD5(数据加密 Key)。实际应答数据视加密设置不同而不同。

| 1 字节     | 1 字节   | 1 字节   | 1 字节     | M 字节   | 余下部分     |
| ---------- | -------- | -------- | ---------- | -------- | ------------ |
| 响应认证 V | 选项 Opt | 指令 Cmd | 指令长度 M | 指令内容 | 实际应答数据 |

其中：

* 响应认证 V：必须和客户端请求中的响应认证 V 一致；
* 选项 Opt：
  * 0x01：服务器端准备重用 TCP 连接（Xray 2.23+ 弃用）；
* 指令 Cmd：
  * 0x01：动态端口指令
* 实际应答数据：
  * 如果请求中的 Opt(S) 开启，则使用标准格式，否则使用基本格式。
  * 格式均和请求数据相同。
    * 当 Opt(M) 开启时，长度 L 的值 = 真实值 xor Mask。Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte()；

### 动态端口指令

| 1 字节 | 2 字节    | 16 字节 | 2 字节  | 1 字节   | 1 字节     |
| ------ | --------- | ------- | ------- | -------- | ---------- |
| 保留   | 端口 Port | 用户 ID | AlterID | 用户等级 | 有效时间 T |

其中：

* 端口 Port：Big Endian 格式的整型端口号；
* 有效时间 T：分钟数；

客户端在收到动态端口指令时，服务器已开放新的端口用于通信，这时客户端可以将数据发往新的端口。在 T 分钟之后，这个端口将失效，客户端必须重新使用主端口进行通信。

## 注释

* 为确保向前兼容性，所有保留字段的值必须为 0。

---

---
url: /development/protocols/muxcool.md
---
# Mux.Cool 协议

Mux.Cool 协议是一个多路复用传输协议，用于在一条已建立的数据流中传输多个各自独立的数据流。

## 版本

当前版本是 1 Beta。

## 依赖

### 底层协议

Mux.Cool 必须运行在一个已建立的可靠数据流之上。

## 通讯过程

一个 Mux.Cool 连接中可传输若干个子连接，每个子连接有一个独立的 ID 和状态。传输过程由帧（Frame）组成，每一帧用于传输一个特定的子连接的数据。

### 客户端行为

当有连接需求时并且没有现有可用的连接时，客户端向服务器发起一个新连接，以下称为“主连接”。

1. 一个主连接可用于发送若干个子连接。客户端可自主决定主连接可承载的子连接数量。
2. 对于一个新的子连接，客户端必须发送状态`New`以通知服务器建立子连接，然后使用状态`Keep`来传送数据。
3. 当子连接结束时，客户端发送`End`状态来通知服务器关闭子连接。
4. 客户端可自行决定何时关闭主连接，但必须确定服务器也同时保持连接。
5. 客户端可使用 KeepAlive 状态来避免服务器关闭主连接。

### 服务器端行为

当服务器端接收到新的子连接时，服务器应当按正常的连接来处理。

1. 当收到状态`End`时，服务器端可以关闭对目标地址的上行连接。
2. 在服务器的响应中，必须使用与请求相同的 ID 来传输子连接的数据。
3. 服务器不能使用`New`状态。
4. 服务器可使用 KeepAlive 状态来避免客户端关闭主连接。

## 传输格式

Mux.Cool 使用对称传输格式，即客户端和服务器发送和接收相同格式的数据。

### 帧格式

| 2 字节       | L 字节 | X 字节   |
| ------------ | ------ | -------- |
| 元数据长度 L | 元数据 | 额外数据 |

### 元数据

元数据有若干种类型。所有类型的元数据都包含 ID 和 Opt 两项，其含义为：

* ID: 子连接的唯一标识
  * 对于一般 Mux 子连接，ID 由 1 开始累加
  * 对于 Xray 实现的 [Single XUDP](https://github.com/XTLS/Xray-core/blob/main/common/xudp/xudp.go)，ID 始终为 0
* Opt:
  * D(0x01): 有额外数据

当选项 Opt(D) 开启时，额外数据格式如下：

| 2 字节   | X-2 字节 |
| -------- | -------- |
| 长度 X-2 | 数据     |

### 新建子连接 (New)

| 2 字节 | 1 字节 | 1 字节   | 1 字节     | 2 字节 | 1 字节     | A 字节 | 8 字节           |
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ | ---------------- |
| ID     | 0x01   | 选项 Opt | 网络类型 N | 端口   | 地址类型 T | 地址 A | Global ID (XUDP) |

其中：

* 网络类型 N：
  * 0x01：TCP，表示当前子连接的流量应当以 TCP 的方式发送至目标。
  * 0x02：UDP，表示当前子连接的流量应当以 UDP 的方式发送至目标。
* 地址类型 T：
  * 0x01：IPv4
  * 0x02：域名
  * 0x03：IPv6
* 地址 A：
  * 当 T = 0x01 时，A 为 4 字节 IPv4 地址；
  * 当 T = 0x02 时，A 为 1 字节长度（L） + L 字节域名；
  * 当 T = 0x03 时，A 为 16 字节 IPv6 地址；
* Global ID (XUDP)：
  * 客户端计算出 UDP 来源二元组的全局独特 ID，服务端用以确保当 XUDP 断线重连时，仍使用同一个端口与目标通信。

在新建子连接时，若 Opt(D) 开启，则这一帧所带的数据需要被发往目标主机。

### 保持子连接 (Keep)

TCP

| 2 字节 | 1 字节 | 1 字节   |
| ------ | ------ | -------- |
| ID     | 0x02   | 选项 Opt |

UDP

| 2 字节 | 1 字节 | 1 字节   | 1 字节     | 2 字节 | 1 字节     | A 字节 |
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ |
| ID     | 0x02   | 选项 Opt | 网络类型 N | 端口   | 地址类型 T | 地址 A |

在保持子连接时，若 Opt(D) 开启，则这一帧所带的数据需要被发往目标主机。
XUDP 在 Opt(D) 之后加 UDP 地址，格式同新建子连接，但没有 Global ID。

### 关闭子连接 (End)

| 2 字节 | 1 字节 | 1 字节   |
| ------ | ------ | -------- |
| ID     | 0x03   | 选项 Opt |

在保持子连接时，若 Opt(D) 开启，则这一帧所带的数据需要被发往目标主机。

### 保持连接 (KeepAlive)

| 2 字节 | 1 字节 | 1 字节   |
| ------ | ------ | -------- |
| ID     | 0x04   | 选项 Opt |

在保持连接时:

* 若 Opt(D) 开启，则这一帧所带的数据必须被丢弃。
* ID 可为随机值。

## 应用

Mux.Cool 协议与底层协议无关，理论上可以使用任何可靠的流式连接来传输 Mux.Cool 的协议数据。

在目标导向的协议如 Shadowsocks 和 VMess 协议中，连接建立时必须包含一个指定的地址。
为了保持兼容性，Mux.Cool 协议指定地址为“v1.mux.cool”。即当主连接的目标地址与之匹配时，则进行 Mux.Cool 方式的转发，否则按传统方式进行转发。（注：这是一个程序内的标记，VMess 和 VLESS 并不会在数据包中发送“v1.mux.cool”地址）

---

---
url: /development/protocols/mkcp.md
---
# mKCP 协议

mKCP 是流式传输协议，由 [KCP 协议](https://github.com/skywind3000/kcp) 修改而来，可以按顺序传输任意的数据流。

## 版本

mKCP 没有版本号，不保证版本之间兼容性。

## 依赖

### 底层协议

mKCP 是一个基于 UDP 的协议，所有通讯使用 UDP 传输。

### 函数

* fnv: [FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) 哈希函数
  * 输入参数为任意长度的字符串；
  * 输入出一个 32 位无符号整数；

## 通讯过程

1. mKCP 将数据流拆成若干个数据包进行发送。一个数据流有一个唯一标识，用以区分不同的数据流。数据流中的每一个数据包都携带了同样的标识。
2. mKCP 没有握手过程，当收到一个数据包时，根据其携带的数据流的标识来判断是否为新的通话，或是正在进行中的通话。
3. 每一个数据包中包含若干个片段（Segment），片段分为三类：数据（Data）、确认（ACK）、心跳（Ping）。每个片段需要单独处理。

## 数据格式

### 数据包

| 4 字节     | 2 字节     | L 字节   |
| ---------- | ---------- | -------- |
| 认证信息 A | 数据长度 L | 片段部分 |

其中：

* 认证信息 A = fnv(片段部分），big endian；
* 片段部分可能包含多个片段；

### 数据片段

| 2 字节    | 1 字节   | 1 字节   | 4 字节    | 4 字节    | 4 字节           | 2 字节   | Len 字节 |
| --------- | -------- | -------- | --------- | --------- | ---------------- | -------- | -------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 时间戳 Ts | 序列号 Sn | 未确认序列号 Una | 长度 Len | 数据     |

其中：

* 标识 Conv: mKCP 数据流的标识
* 指令 Cmd: 常量 0x01
* 选项 Opt: 可选的值有：
  * 0x00: 空选项
  * 0x01: 对方已发出所有数据
* 时间戳 Ts: 当前片段从远端发送出来时的时间，big endian
* 序列号 Sn: 该数据片段时数据流中的位置，起始片段的序列号为 0，之后每个新片段按顺序加 1
* 未确认序列号 Una: 远端主机正在发送的，且尚未收到确认的最小的 Sn

### 确认片段

| 2 字节    | 1 字节   | 1 字节   | 4 字节   | 4 字节            | 4 字节    | 2 字节   | Len \* 4 字节  |
| --------- | -------- | -------- | -------- | ----------------- | --------- | -------- | -------------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 窗口 Wnd | 下一接收序列号 Sn | 时间戳 Ts | 长度 Len | 已收到的序列号 |

其中：

* 标识 Conv: mKCP 数据流的标识
* 指令 Cmd: 常量 0x00
* 选项 Opt: 同上
* 窗口 Wnd: 远端主机可以接收的最大序列号
* 下一接收序列号 Sn: 远端主机未收到的数据片段中的最小序列号
* 时间戳 Ts: 远端主机最新收到的数据片段的时间戳，可用于计算延迟
* 已收到的序列号: 每个 4 字节，表示此序列号的数据已经确认收到

注释：

* 远程主机期待收到序列号 \[Sn, Wnd) 范围内的数据

### 心跳片段

| 2 字节    | 1 字节   | 1 字节   | 4 字节           | 4 字节            | 4 字节   |
| --------- | -------- | -------- | ---------------- | ----------------- | -------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 未确认序列号 Una | 下一接收序列号 Sn | 延迟 Rto |

其中：

* 标识 Conv: mKCP 数据流的标识
* 指令 Cmd: 可选的值有
  * 0x02: 远端主机强行终止会话
  * 0x03: 正常心跳
* 选项 Opt: 同上
* 未确认序列号 Una: 同数据片段的 Una
* 下一接收序列号 Sn: 同确认片段的 Sn
* 延迟 Rto: 远端主机自己计算出的延迟

---

---
url: /config/inbounds/dokodemo.md
---
# Dokodemo-Door

See [Tunnel](./tunnel.md)

---

---
url: /config/transports/http.md
---
# HTTP

See [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

---

---
url: /config/transports/h2.md
---
# HTTP/2

See [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

---

---
url: /config/transports/quic.md
---
# QUIC

See [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

---

---
url: /config/transports/splithttp.md
---
# SplitHTTP

See [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)

---

---
url: /config/transports/tcp.md
---
# TCP

See [RAW](./raw.md)

---

---
url: /config/reverse.md
---
# 反向代理

::: danger
此功能已废弃，请使用 VLESS 反向代理
:::

反向代理可以把服务器端的流量向客户端转发，即逆向流量转发。

::: tip
这个反向代理为通用反向代理（不限制代理协议类型），配置更为复杂，不要和 VLESS 简易配置反向弄混（见 VLESS 出入站文档相关部分）。
:::

其底层协议为 Mux.cool, 不过方向是相反的，服务端向客户端发起请求。

反向代理的大致工作原理如下:

* 假设在主机 A 中有一个网页服务器，这台主机没有公网 IP，无法在公网上直接访问。另有一台主机 B，它可以由公网访问。现在我们需要把 B 作为入口，把流量从 B 转发到 A。
  * 在主机 B 中配置 Xray，接收外部请求，所以称为 `portal` （门户）。
  * 在主机 A 中配置 Xray，负责将B的转发和网页服务器桥接起来，称为`bridge`。

* `bridge`
  * `bridge` 会向 `portal` 主动建立连接以注册反向通道，此连接的目标地址（domain）可以自行设定。
  * `bridge` 在收到`portal`转发过来的公网流量之后，会将其原封不动地发给主机 A 中的网页服务器。当然，这一步需要路由模块的配置。
  * `bridge` 收到响应后，也会将响应原封不动地返回给`portal`。

* `portal`
  * `portal` 收到请求且domain匹配，则说明是由 `bridge` 发来的响应数据，这条连接会用于建立反向通道。
  * `portal` 收到请求，domain不匹配，则说明是公网用户发来的连接，这种连接数据会转发给bridge.

* `bridge` 会根据流量的大小进行动态的负载均衡。

::: tip
如上所述，反向代理默认已开启 [Mux](../development/protocols/muxcool.md)，请不要在其用到的 outbound 上再次开启 Mux。
:::

::: warning
反向代理功能尚处于测试阶段，可能会有一些问题。
:::

## ReverseObject

`ReverseObject` 对应配置文件的 `reverse` 项。

```json
{
  "reverse": {
    "bridges": [
      {
        "tag": "bridge",
        "domain": "reverse-proxy.xray.internal"
      }
    ],
    "portals": [
      {
        "tag": "portal",
        "domain": "reverse-proxy.xray.internal"
      }
    ]
  }
}
```

> `bridges`: \[[BridgeObject](#bridgeobject)]

数组，每一项表示一个 `bridge`。每个 `bridge` 的配置是一个 [BridgeObject](#bridgeobject)。

> `portals`: \[[PortalObject](#portalobject)]

数组，每一项表示一个 `portal`。每个 `portal` 的配置是一个 [PortalObject](#bridgeobject)。

### BridgeObject

```json
{
  "tag": "bridge",
  "domain": "reverse-proxy.xray.internal"
}
```

> `tag`: string

所有由 `bridge` 发出的连接，都会带有这个标识。可以在 [路由配置](./routing.md) 中使用 `inboundTag` 进行识别。

> `domain`: string

指定一个域名，`bridge` 向 `portal` 建立的连接，都会借助这个域名进行发送。
这个域名只作为 `bridge` 和 `portal` 的通信用途，不必真实存在。

### PortalObject

```json
{
  "tag": "portal",
  "domain": "reverse-proxy.xray.internal"
}
```

> `tag`: string

`portal` 的标识。在 [路由配置](./routing.md) 中使用 `outboundTag` 将流量转发到这个 `portal`。

> `domain`: string

一个域名。当 `portal` 接收到流量时，如果流量的目标域名是此域名，则 `portal` 认为当前连接上是 `bridge` 发来的通信连接。而其它流量则会被当成需要转发的流量。`portal` 所做的工作就是把这两类连接进行识别并做对应的转发。

::: tip
一个 Xray 既可以作为 `bridge`，也可以作为 `portal`，也可以同时两者，以适用于不同的场景需要。
:::

## 完整配置样例

::: tip
在运行过程中，建议先启用 `bridge`，再启用 `portal`。
:::

### bridge 配置

`bridge` 通常需要两个 outbound，一个用于连接 `portal`，另一个用于发送实际的流量。也就是说，你需要用路由区分两种流量。

反向代理配置:

```json
"reverse": {
  "bridges": [
    {
      "tag": "bridge",
      "domain": "reverse-proxy.xray.internal"
    }
  ]
}
```

outbound:

```json
{
  // 转发到网页服务器
  "tag": "out",
  "protocol": "freedom",
  "settings": {
    "redirect": "127.0.0.1:80",
    // 仅放行 TCP 127.0.0.1:80，阻止其它目标
    "finalRules": [
      {
        "action": "allow",
        "network": "tcp",
        "port": 80,
        "ip": ["127.0.0.1/32"]
      },
      {
        "action": "block"
      }
    ]
  }
}
```

```json
{
  // 连接到 portal
  "protocol": "vmess",
  "settings": {
    "address": "portal 的 IP 地址",
    "port": 1024,
    "id": "5783a3e7-e373-51cd-8642-c83782b807c5"
  },
  "tag": "interconn"
}
```

路由配置:

```json
{
  "rules": [
    {
      // bridge 发出的请求，且域名为配置的域名，那么说明这是尝试向 portal 建立反向隧道的请求，
      // 则路由到 interconn，即连接到 portal
      "inboundTag": ["bridge"],
      "domain": ["full:reverse-proxy.xray.internal"],
      "outboundTag": "interconn"
    },
    {
      // 从 portal 过来的流量，也会从 bridge 出来，但是不带上面的domain
      // 则路由到 out，即转发给网页服务器
      "inboundTag": ["bridge"],
      "outboundTag": "out"
    }
  ]
}
```

### portal 配置

`portal` 通常需要两个 inbound，一个用于接收 `bridge` 的连接，另一个用于接收实际的流量。同时你也需要用路由区分两种流量。

反向代理配置:

```json
"reverse": {
  "portals": [
    {
      "tag": "portal",
      "domain": "reverse-proxy.xray.internal" // 必须和 bridge 的配置一样
    }
  ]
}
```

inbound:

```json
{
  // 直接接收来自公网的请求
  "tag": "external",
  "port": 80,
  "protocol": "dokodemo-door",
  "settings": {
    "allowedNetwork": "tcp",
    "rewriteAddress": "127.0.0.1",
    "rewritePort": 80
  }
}
```

```json
{
  // 接收来自 bridge 尝试建立反向隧道的请求
  "tag": "interconn",
  "port": 1024,
  "protocol": "vmess",
  "settings": {
    "users": [
      {
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5"
      }
    ]
  }
}
```

路由配置:

```json
{
  "rules": [
    {
      // 如果入站是 external，说明是来自公网的请求，
      // 则路由到 portal, 最终会转发给 bridge
      "inboundTag": ["external"],
      "outboundTag": "portal"
    },
    {
      // 如果来自 interconn 入站，说明是来自 bridge 的尝试建立反向隧道请求，
      // 则路由到 portal, 最终会转发给对应的公网客户端
      // 注意：这里进入的请求会带上了前文配置的domain，所以 portal 能够区分两种被路由到 portal 的请求
      "inboundTag": ["interconn"],
      "outboundTag": "portal"
    }
  ]
}
```

---

---
url: /about/news.md
---
# 大史记

## 2024.10.31 [24.10.31](https://github.com/XTLS/Xray-core/releases/tag/v24.10.31)

万圣节快乐！ 🎃

本次万圣节可是有礼物和小糖果的喔~欢迎拆开品尝~ 🎁

\~~SplitHTTP 进化为 XHTTP，而且现在更有上下行分离的能力了！毫无疑问 XHTTP 又开启了一个崭新的时代。~~

## 2024.10.24

现在只列出能安全配置的面板。明文 HTTP 问题不能轻视。

## 2024.10.18

昨天已售出第一个 0.15 ETH 的 Project X NFT，感谢支持！别忘了买得越早越便宜喔。

REALITY NFT 预计有一万个（送两个），0.01 ETH 一个，以便作为日常捐款途径。

## 2024.10.4

为什么会有在公网用 http://ip 来管理面板的？不，这不应该……

安全起见，这类配置用的工具应该用 HTTPS 或者 SSH 转发来[保证安全](https://t.me/projectXtls/358)。

## 2024.10.3

v24.9.30 被设为 latest 已经快一天了，根本没看到有人抱怨问题 ~~，说明那些被删掉的东西真的没人用~~。🤡

## 2024.9.30 [24.9.30](https://github.com/XTLS/Xray-core/releases/tag/v24.9.30)

更改版本号规则之后的第一次发稳定版！

作为更改版本号后的开山第一版：

* SplitHTTP 加入了 XMUX 控制
* HTTP 传输层加入了 HTTP/3
* 传输层的 TCP 更名为 RAW （现在变得更合理了）
* Freedom 出站的 UDP Noises
* **注意**：该版本移除了大量的过时配置代码，升级前请根据文档严格检查配置文件避免意外喔。

## 2024.9.28

入群问题真的公认的难。

不过人不在多而在精，考验的就是独立查找问题答案的能力 ~~以及一点点运气~~，这样才能保持群的高质量交流。

珍惜留在群里的资格不要违规喔。

## 2024.9.24

提醒：价格为 0.1 ETH 的 Project X NFT 仅剩一个，此后价格为 0.15 ETH 起步。

## 2024.9.17

姊妹群组 Project X 成员数达到 1000。

感谢来自非中文世界的支持！

* XHTTP 和 XgRPC 是什么？

## 2024.9.12

大史记重出江湖？！

## 2024.9.7 [v24.9.7](https://github.com/XTLS/Xray-core/releases/tag/v24.9.7)

更改版本号之后的首次发版。

* 这次移除了 QUIC 以及 DomainSocket 传输，移除了两处远古配置遗留代码。
  * 二进制大小比 v1.8.24 减小了 1MB。
* 依然有每次必备的 bug 修复。

## 2024.9.6

Xray-core 能使用 JSON 格式订阅吗？提案[在此](https://github.com/XTLS/Xray-core/discussions/3765)。

## 2024.8.30 [v1.8.24](https://github.com/XTLS/Xray-core/releases/tag/v1.8.24)

在等待 SplitHTTP multiplex controller 期间，main 分支已经积累了大量重要更新，所以我们决定先发一个版本。

* Socks 入站现在默认兼容 HTTP 代理请求。
* UDP noise (preview)
* 还有一些改进。

由于传统版本号的存在，为每个版本规划功能、进行排期已经严重阻碍了新功能的开发、合并、发布。所以我们决定从下个版本开始弃用传统的版本号，改用发版日期作为版本号，如 v24.8.30，并取消版本规划，全面采用流式更新，写好的功能直接合并，不再等待，预计每月月底发一个版本。

毕竟对于反审查软件来说，相较于传统的版本号，新功能的及时性、每月更新更为重要，而不是发一个功能确定的版本并长期维护。

下个版本会移除一些历史久远的代码，以后日常积累新代码、提醒迁移，跨年新版删代码、breaking。

我们相信有了各位的捐款以及对发版形式的革新，Xray-core 这个项目会发展得更好。

## 2024.8.26

Project VLESS 群组创立。

We have created [Project VLESS](https://t.me/projectVless) for non-Chinese users (mainly Russian).

## 2024.8.3

第一个 [Project X NFT](https://github.com/XTLS/Xray-core/discussions/3633) 正式发行！

就像 Xray 开创过很多历史一样，发行 NFT 也是这个领域前无古人的操作。这些 NFT 非常有纪念意义，甚至可以说是有历史意义，远大于现在的初始价格，假以时日它们必将价值连城。最后再次感谢大家对 Project X 的支持。

## 2024.7.29 [v1.8.23](https://github.com/XTLS/Xray-core/releases/tag/v1.8.23)

* 恭喜 [@mmmray](https://github.com/mmmray) 贡献了 Xray-core 的第 1000 个 commit！
* 优化了 SplitHTTP 上行的稳定性，服务端必须升级到该版本以支持新版客户端。
* 更多 SplitHTTP 上的变化。

## 2024.7.22 [v1.8.21](https://github.com/XTLS/Xray-core/releases/tag/v1.8.21)

中间似乎回到了最初的腹泻式发版状态……

正如 v1.8.16 所预告的，SplitHTTP 现已初步支持 HTTP/3（QUIC）。毫无疑问，SplitHTTP H3 已经开启了一个崭新的时代。

* SplitHTTP H3 是第一个完全基于标准 H3、支持套 CDN 的 QUIC 类代理，亦可用反代、Browser Dialer 来隐蔽自身。

## 2024.7.16

Project X 文档迎来了俄语版！感谢 [@iambabyninja](https://github.com/iambabyninja) 的翻译！

> Привет, друзья из России!

## 2024.7.15

通过已知信息以及努力，Xray-core 现在重新支持 Windows 7！在后续的发版中，Windows 7 用户下载名为 Xray-win7-32.zip 或 Xray-win7-64.zip 的压缩包解压即可享受，感谢大家的支持！具体使用方式请点[这里](../document/install.md)

虽然日后随着各方面升级 Windows 7 最终会离开，但是现在还是可以让这个时间来得稍微晚一些。

## 2024.6.18 [v1.8.16](https://github.com/XTLS/Xray-core/releases/tag/v1.8.16)

新传输来了，它目前叫 SplitHTTP。

* 实现进一步的流量混淆有两种刚好相反的方式：多路复用与拆分连接。
* 可以通过不支持 WebSocket、gRPC 的 CDN，实现与 Meek 相同的目标，且 SplitHTTP 比 Meek 更简单、效率更高。
* SplitHTTP 没有 WebSocket 的 ALPN 问题，这是一大优势，未来还会支持 HTTP/3（QUIC）。
* 另外 SplitHTTP 也已经加入分享链接套餐~

## 2024.6.2

一个新的传输方式正在被打造……

## 2024.4.26 [v1.8.11](https://github.com/XTLS/Xray-core/releases/tag/v1.8.11)

* 现在有了生成 ECH 密钥的工具。
* 增强、修复，并移除了一点不再使用的代码。

## 2024.4.20

我们现在有了 issues 模板，感谢 [@Fangliding](https://github.com/Fangliding) ！

## 2024.4.13

VLESS Seed 整备完毕，待势而发。

## 2024.3.18 [v1.8.10](https://github.com/XTLS/Xray-core/releases/tag/v1.8.10)

和 WebSocket 一样，HTTPUpgrade 也有 0-RTT 了。

## 2024.3.11 [v1.8.9](https://github.com/XTLS/Xray-core/releases/tag/v1.8.9)

新增 HTTPUpgrade 传输，听说比 WebSocket 要轻。

* 已加入分享链接套餐~

## 2024.2.29

gRPC 传输现在也有 Host 一样的配置字段了！它叫 `authority`。这下 gRPC 也能“域前置”了，没有 ALPN 问题。

## 2024.2.25 [v1.8.8](https://github.com/XTLS/Xray-core/releases/tag/v1.8.8)

* 现在 XUDP 流量统一使用 Vision 填充了，速来体验。
* 新增了 leastLoad balancer。
* 修复错误、优化性能……

## 2024.1.9

惊闻 Win7 无法运行新版 Xray-core？探索之下竟发现 Go 放弃了对 Win7 的支持。有什么办法能继续支持这个有些古老但是依然优雅的操作系统吗？

## 2023.11.21

发表在 USENIX 顶会的[论文](https://t.me/projectXtls/212)证实，XTLS Vision 已经达到它的设计目标。

而 XTLS 也不会止步于此，如 X 射线一般穿破高耸的围墙。

## 2023.11.18 [v1.8.6](https://github.com/XTLS/Xray-core/releases/tag/v1.8.6)

* WireGuard 现在也有了对应的入站。Freedom 出站也终于有了 splice。
* 现在出站的 domainStrategy 也得到了统一。
* 更多的美味小点心。
* 因为 ~~不可抗力~~ 功能变更，Dragonfly BSD 支持黯然离场。
* \~~我们真的要对一代经典 Windows 7 说再见了吗？~~

## 2023.9.30

为 v2rayNG 设计了全新的配色，安装最新的 Pre-release 版本即可体验。

## 2023.8.29 [v1.8.4](https://github.com/XTLS/Xray-core/releases/tag/v1.8.4)

1.8.x 在经过半年的打磨后终于来到了第一个认可的正式版了。

同样地，这次集成的改进也不少，速来品尝！

## 2023.7.22

又修好了一个 HTTP/2 传输的历史遗留断流问题。

## 2023.7.7

即将给 Vision 添加 Seed 支持。

## 2023.6.30

下一个 XTLS 流控：xtls-rprx-switch 🍪

* XTLS 的 0-RTT 已经预告几个月了，本来也是想保留神秘感。
* 对比现有的 XTLS Vision 和 Mux 有着更加不错的优势。

## 2023.6.27

[如何选取 REALITY 目标域名？来看这里助你事半功倍！](https://github.com/XTLS/Xray-core/discussions/2256#discussioncomment-6295296)

## 2023.6.19 [v1.8.3](https://github.com/XTLS/Xray-core/releases/tag/v1.8.3)

* 精简代码计划后的第一个版本，VMess (MD5)、MTProto 以及 Starlark 相关代码已被卸下。轻装上阵。
* 对代码进重构也是轻装上阵的一部分。
* 同时我们也没有忘记增添一些增强功能，还有修复漏洞。
* \~~v1.8.3 为今年的最后一个版本。~~

## 2023.6.6

好消息：下一个 XTLS 流控不叫 Vision。 🍪

## 2023.4.21

也许我们可以借助一下 [RealiTLScanner](https://github.com/XTLS/RealiTLScanner)……

## 2023.4.20

经过长年累月的开发，累积代码不计其数…… [精简代码计划](https://github.com/XTLS/Xray-core/discussions/1967) 被提出了！

## 2023.4.19

`xtls-0rtt-vision(-udp443)` 🍪

## 2023.4.18 [v1.8.1](https://github.com/XTLS/Xray-core/releases/tag/v1.8.1)

升级后的 XUDP 也来了！

* 现在 XUDP 也带有连接迁移、端口复用的特性，并且带有全局 Session ID ~~，麻麻再也不用担心意外断线的时候怎么办了~~ 。
* 同时我们也添加了 XUDP 的控制配置，让你能更好掌控它~
* 新的 XUDP 配合 XTLS Vision 食用风味更好喔~
* 惯例还有小甜点，欢迎品尝~

## 2023.4.6

XUDP 也在悄然升级……

## 2023.3.29

`PLUX protocol` 🍪

## 2023.3.19

对 REALITY 的分享链接标准也已经出现了。

## 2023.3.9 [v1.8.0](https://github.com/XTLS/Xray-core/releases/tag/v1.8.0)

> THE NEXT FUTURE, REALITY is NOW release on Xray-core

REALITY 已经实装发版！欢迎体验！
XTLS Vision 也已经完善，请两端升级至最新版食用。

* 因为这次 Vision 填充算法改变，XTLS Vision 旧版和新版之间会存在兼容性问题。
* HTTP/2 传输也已经做了改善，现在使用新版即可纵享丝滑~
* 还有大量小改进欢迎体验~

## 2023.3.4

> Legends never die, they become a part of ~~you~~ VLESS.
>
> They simply fade away.

## 2023.3.2

HTTP/2 传输的一些遗留问题已经被改善，欢迎搭配 REALITY 测试纵享丝滑~

## 2023.2.16

THE NEXT FUTURE becomes THE REALITY NOW!

## 2023.2.9

REALITY is reality now!

## 2023.2.8 [v1.7.5](https://github.com/XTLS/Xray-core/releases/tag/v1.7.5)

Keep riding and never look back.

* 恭喜 [@yuhan6665](https://github.com/yuhan6665) 贡献了 Xray-core 的第 500 个 commit！
* XTLS Vision 流控已经接近完善，即将实用。
* 现在对 uTLS 指纹模拟添加了更多可选项，有哪一款适合你？
* 分享链接也支持同时分享 uTLS 指纹配置了。
* 还有更多的功能增强和修复。
* 这一版也是能最后一次看到 XTLS Origin、Direct 和 Splice 流控的一版了。 ~~有点伤感不是吗？~~

## 2023.1.29

Winter cannot cover the NEXT FUTURE...

## 2022.12.26 [v1.7.0](https://github.com/XTLS/Xray-core/releases/tag/v1.7.0)

因为手滑，这次的版本号直接大升，感谢大家支持！

* 以后将会严格执行 Semantic Versioning。

## 2022.11.28 [v1.6.5](https://github.com/XTLS/Xray-core/releases/tag/v1.6.5)

这次我们有了 WireGuard 出站。

* 使用 WireGuard 搭配 CF WARP 使用可以解锁有趣的新玩法呢。
* 同样安全更新和修复也不会少。

## 2022.11.7 [v1.6.3](https://github.com/XTLS/Xray-core/releases/tag/v1.6.3)

现在 Vision 流控也能使用 uTLS 指纹模拟了，这就是使用 `tlsSettings` 带来的好处吗！

## 2022.10.29 [v1.6.2](https://github.com/XTLS/Xray-core/releases/tag/v1.6.2)

第一个包含 Vision 流控的发行版已经放出！欢迎试用并提交反馈！

## 2022.10.22 [v1.6.1](https://github.com/XTLS/Xray-core/releases/tag/v1.6.1)

* 为 WebSocket、HTTP/2 以及 gRPC 传输带来了 uTLS 指纹支持！
  * 之前只有普通 TLS 下 TCP 传输能用的选项现在更好用了。
* Linux 下可以单独为出入口设置 TCP 拥塞控制了。

## 2022.10.3

天气渐凉，但是并没有凉下开发的脚步。封锁天降，但无法阻止前行……

* 新的 XTLS 流控酝酿中……
  * 解决之前流控已有的问题；
  * 对 TLS 1.3 直接启用 splice；
  * 增加 TLS 握手长度混淆；
  * 简化代码，使用 `tlsSettings` 而不是 `xtlsSettings`……

## 2022.8.28 [v1.5.10](https://github.com/XTLS/Xray-core/releases/tag/v1.5.10)

底层传输支持更合理的 TCP Keepalive 配置了。

## 2022.6.20 [v1.5.8](https://github.com/XTLS/Xray-core/releases/tag/v1.5.8)

现在 Shadowsocks-2022 的 relay 中转也受支持了。

## 2022.5.29 [v1.5.6](https://github.com/XTLS/Xray-core/releases/tag/v1.5.6)

Shadowsocks-2022 协议来到了 Xray-core！

* 感谢 [@nekohasekai](https://github.com/nekohasekai) 开发全新 go 实现 https://github.com/SagerNet/sing-shadowsocks 并引入 Xray。
* 感谢 [@database64128](https://github.com/database64128) 推动 Shadowsocks 社区提出完整设计方案。
* 感谢 [@RPRX](https://github.com/RPRX) 提交原始漏洞。

Shadowsocks-2022 是重新设计的全新协议：

* 在保留 Shadowsocks 原生 udp 的基础上解决了重放攻击等安全问题（与 vmess 一样使用时间戳，因此客户端与服务端需要时间一致）。
* 支持单端口多用户，并且参考 quic、wireguard 等协议设计与实现使用了 session 机制，减低加密负担，保证网络变动时的无缝迁移。

## 2022.4.24 [v1.5.5](https://github.com/XTLS/Xray-core/releases/tag/v1.5.5)

这次带来了方便可视化的检测数据接口！快来体验！

* 顺便修复了一些影响使用体验的问题。

## 2022.3.13 [v1.5.4](https://github.com/XTLS/Xray-core/releases/tag/v1.5.4)

给 Windows 平台加上了没有黑窗冒出的 wxray.exe 文件，并带来了对 UDS 监听的增强。

## 2022.1.29 [v1.5.3](https://github.com/XTLS/Xray-core/releases/tag/v1.5.3)

牛辞胜岁，虎跃新程。🧨

* 这次带来了对 QUIC 传输的流分配改进，使用 QUIC 传输现在更丝滑了。

## 2021.12.24 [v1.5.2](https://github.com/XTLS/Xray-core/releases/tag/v1.5.2)

为 gRPC 添加了一个新的选项，在通过 CDN 时变得更好用了。

## 2021.12.15 [v1.5.1](https://github.com/XTLS/Xray-core/releases/tag/v1.5.1)

> “过渡时期的阶段性的维护版本”

* 新功能、增强还有大量修复陆续有来。
* 记得将 VMess 配置中的 `alterID` 去掉！

## 2021.10.20 [v1.5.0](https://github.com/XTLS/Xray-core/releases/tag/v1.5.0)

真的是巨大的改动！

* 重构了 DNS 组件，支持的协议和细化配置更多了。
* 增强了 gRPC 传输以及 FakeDNS。
* 现在终于支持 Windows ARM64 了。
* 更多新功能和改进等待体验。

## 2021.9.23 [v1.4.5](https://github.com/XTLS/Xray-core/releases/tag/v1.4.5)

中秋快乐，阖家团圆。

* \~~修正了版本号过低，版本号不吉利的 bug。~~
* 这次移除了 Shadowsocks 里面已经不安全的加密方式。要尽快迁移到 AEAD 加密上面喔。
* 这次修复了远古时期开始就存在的历史问题：开启流量统计功能可能会使性能下降。简单来说，不论什么配置现在打开统计都不会对性能有任何影响了。
* 还有对 XTLS 的安全性更新以及大量修复。
* 对了，因为 TLS 库的更新，`cipherSuites` 不能再指定加密套件顺序了，而 `preferServerCipherSuites` 已经被彻底弃用。事实上这些变化在 Xray-core v1.4.3 中已经产生了。

## 2021.9.16

* 文档站已经完全切换到 docs-next，丝般顺滑，体验更好！地址仍为 <https://xtls.github.io/>

## 2021.9.8 [v1.4.3](https://github.com/XTLS/Xray-core/releases/tag/v1.4.3)

这是一个阶段性维护版本。开发仍在继续……

* 在此期间累积了大量改进和新功能。
* 加入新的 DomainMatcher，现在域名规则匹配性能更好了。
* 加入对 HTTP/2 和 gRPC 传输的健康检查、对未知 SNI 的处理改进，以及修复了一大堆 bug。

> \~~Helden sterben nicht!~~

## 2021.7.14

* AnXray ~~重金设计~~ 的新图标已经上线！
  * 现在图标的辨识度更高了。
* 过去三个星期，AnXray 共积累了 600 stars、2K+ 频道订阅数和 11K+ GitHub 下载量，感谢大家的支持。
* AX 为 AnXray 的缩写，推荐用 AX 指代 AnXray，简短方便

## 2021.6.21

现在一个以 Xray-core 为核心的开源、自由的 Android 客户端已经出现——[AnXray](https://github.com/XTLS/AnXray)！由 [@nekohasekai](https://github.com/nekohasekai) 维护。

* 支持众多协议、插件.
* 首席视觉设计师 [@RPRX](https://github.com/RPRX) 设计了 X-style 的 logo、slogan，以及独一无二的 material 黑白主题。
* APP 内还有个小彩蛋等你去发现。

前两天从早到晚反复打磨细节，希望大家多多 Star、关注。

## 2021.5.1

对 tun2socks 的改进出现在 v2rayNG 上面了。

## 2021.4.26

给 tun2socks 带来了一个改进。后续有可能能吃到它~

## 2021.4.12

现在带来了 X-flutter 前瞻，可以期待一下会是什么样子呢~ ~~🍪~~

## 2021.4.6

* VuePress Next.
* With Dark Mode.

## 2021.4.4

* 本文档迎来的新的首页。
* 本文档迎来了暗黑模式。
* \~~当然，暗黑模式还有各种各样的问题。具体的内容还需要慢慢调整。~~
* 另：Telegram 群聊突破了 5000 人！还加入了 Anti-Spam 机器人！
* 🎉🎉🎉

## 2021.4.1 [v1.4.2](https://github.com/XTLS/Xray-core/releases/tag/v1.4.2)

* 不是愚人节玩笑，今天更新。
* 加入 Browser Dialer，用与改变 TLS 指纹与行为。
* 加入 uTLS，用与改变 TLS Client Hello 的指纹。
* 顺便修复了一大堆奇妙的问题，具体的内容见更新日志。

## 2021.3.25

没错还在变。 -\_-

## 2021.3.15

文档网站正在悄悄的进行着某些神秘的变化。。。，🙊🙊🙊

## 2021.3.14 [v1.4.0](https://github.com/XTLS/Xray-core/releases/tag/v1.4.0)

* Happy Pi-Day!
* 这次是个大更新：
  * 为链式代理引入了传输层支持。
  * 为 Dialer 引入了 Domain Strategy，解决奇妙的 DNS 问题。
  * 添加了 gRPC 传输方式，与更快一点的 Multi Mode。
  * 添加了 WebSocket Early-Data 功能，减少了 WebSocket 的延迟。
  * 添加了 FakeDNS。
  * 还修复了系列的问题，添加了各类功能，详情请见更新日志。
* 还是 VuePress 比较爽啊（

## 2021.3.3 [1.3.1](https://github.com/XTLS/Xray-core/releases/tag/v1.3.1)

* 这个版本使用了 Golang 1.16，正式原生支持 Apple Silicon。
* 同时修复了一个会导致 Panic 的 bug。~~Holmium\_认为这是在骗、在偷袭。~~
* 修复了几个遗留问题。

## 2021.2.14 [1.3.0](https://github.com/XTLS/Xray-core/releases/tag/v1.3.0)

* Happy 🐮 Year 🎉！
* v1.3.0 通过非常巧妙的机制实现了 V 系协议全部 FullCone，同时保证了一定的兼容性。
* OHHHHHHHHHHHH！

## 2021.01.31 [1.2.4](https://github.com/XTLS/Xray-core/releases/tag/v1.2.4)

* 解决两个“连接至标准 Socks 服务端时可能出错”的历史遗留问题。
* 似乎这个版本没有什么改变，但这只是暴风雨前的宁静。
* （没错我就是先知）
  > 你个傻子，你拿的是 UNO 牌。

## 2021.01.25

* 全互联网最好最详细的秘籍入门篇同学们练熟了吗? 🍉 老师开始连载[秘籍第一层](../document/level-1/)咯...
* [英文版文档网站](/en/)逐渐增加内容 ing, 感谢各位大佬的辛苦付出~!

## 2021.01.22 [1.2.3](https://github.com/XTLS/Xray-core/releases/tag/v1.2.3)

* 对 SS 协议的支持**又**变强了, 支持单端口多用户!
* 对 trojan 协议的支持也**又**变强了, trojan 的回落也解锁 SNI 分流的新姿势啦~!
* *(VLESS: 嘤嘤嘤)*
* UDP 奇奇怪怪的 BUG 被干掉了, 一个字, "稳定".
* 嗅探可以排除你不想嗅探的域名, 可以开启一些新玩法.
* 向发现问题->开 issue->自行测试->自行分析->自行找到问题->自行解决->然后给上下游提交 PR 的大佬 [@Bohan Yang](https://github.com/bohanyang) 致敬!
* 其他美味小樱桃, 惯例更新品尝就对啦.

## 2021.01.19

* 一些数字
  * 版本发布了 10   个 tag
  * 解决掉了 100  个 issue
  * 复刻了 300  个 fork
  * 点了 2000 个 star
  * 群 3000 个 人

## 2021.01.17

* 辛苦的翻译工作开始了, 感谢 [@玖柒 Max](https://github.com/jiuqi9997)和其他所有的翻译大佬们.
* [English version](/en/)

## 2021.01.15 [1.2.2](https://github.com/XTLS/Xray-core/releases/tag/v1.2.2)

* 回落分流又解锁了奇怪的新姿势! 回落中可以根据 SNI 分流啦~!
* 之前预告的 UUID 修改正式上线.([往下看往下看](#_2021-01-12))
* 日志现在看起来比上一次顺眼又更顺眼了一丢丢.
* 远程 DOH 和其他的 DNS 模式一样学会了走路由分流.
* 当然还有其他各种小糖果.(更新品尝就对了)
* 啊, 还有, 世界上第一個 M1 上跑起 Xray 的男人是 Anthony TSE

## 2021.01.12

* 将要到来的 UUID 修改, 支持自定义字符串和 UUID 之间的映射. 这意味着你将可以这样在配置文件中写 id 来对应用户.
  * 客户端写 "id": "我爱 🍉 老师 1314",
  * 服务端写 "id": "5783a3e7-e373-51cd-8642-c83782b807c5" (此 UUID 是 `我爱🍉老师1314` 的 UUID 映射)
* 🍉 老师的[小小白白话文](../document/level-0/)大结局, 撒花.

## 2021.01.10 [1.2.1](https://github.com/XTLS/Xray-core/releases/tag/v1.2.1)

* [小小白白话文](../document/level-0/)连载上线啦,🍉 老师呕心沥血之作, 手把手教你从什么都不会到熟练配置 Xray!
* (可能是整个互联网上, 最详细最有耐心的教你从 0 开始配置的教程)
* [透明代理](../document/level-2/)也增加了更多文章.
* 还有很多细节修改, 文档将会越来越规范!
* 感谢 [@ricuhkaen](https://github.com/ricuhkaen) , [@BioniCosmos](https://github.com/BioniCosmos), [@kirin](https://github.com/kirin10000)

- 大量的 UDP 相关修复, 甚至可以在育碧的土豆服务器上玩彩虹六号!
- Google Voice 应该也可以正常使用 v2rayNG 拨打了.
- 日志现在看起来更顺眼.

## 2021.01.07

* 礼貌和尊重本应是社区不需要明说的准则之一。

## 2021.01.05

* 文档网站正在悄悄的进行着某些神秘的变化。。。，🙊🙊🙊

## 2021.01.03

* 文档仓库第一个 PR。🎉
  [透明代理（TProxy）配置教程 ](../document/level-2/tproxy.md) ，感谢 [@BioniCosmos](https://github.com/BioniCosmos)
* tg 群突破 2500。

## 2021.01.01

【祝大家新年快乐，嗨皮牛耶！】🎆🎇🎆 [1.2.0](https://github.com/XTLS/Xray-core/releases/tag/v1.2.0)

🎁 在元旦的最后几分钟，v1.2.0 它来了，带着周五必更的惯例，带着各位贡献大佬的心血以及 @rprxx 的黑眼圈，不负众望的来了!

* 圣诞礼物[v1.1.5](#_2020-12-25-1-1-5)后的元旦礼物 🎁，游戏玩家大福利，全面 FullCone。
* （UDP 还会继续增强！）
* 如果你已经拆过圣诞礼物，这次还有比圣诞礼物更精美的包装和小糖果哦。（同样不用问，更新品尝就对了）
* （不，下面不是广告，是里程碑。）
* Xray 是有史以来第一个不受限制的多协议平台：只需 Xray 即可解决问题，无需借力其它实现。
  * 一人扛起了所有！支持各大主流协议！
  * 一骑绝尘的性能!
  * 日趋完善的功能!
  * 可怕的生命力与社区亲和力！
* Xray 将继续保持前行！ 因此 [Xray 需要更多的英雄！！](https://github.com/XTLS/Xray-core/discussions/56)！
* PS：请品，请细品[release notes](https://github.com/XTLS/Xray-core/releases/tag/v1.2.0)每一句。似乎有一个小秘密小彩蛋 ~~（啊，有人敲门...我一会和你们说）~~

## 2020.12.29

透明代理的游戏玩家利好！ Xray-core tproxy 入站， socks 出站 UDP FullCone 测试版, [TG 群](https://t.me/projectXray)火热测试中

## 2020.12.25 [1.1.5](https://github.com/XTLS/Xray-core/releases/tag/v1.1.5)

圣诞节快乐！

* 游戏玩家的圣诞礼物！你可以用 xray 爽快的打游戏啦！因为有了 SS/trojan UDP fullcone
* 你可以用你喜欢的格式写配置文件了，比如 yaml，比如 toml...
* （VLESS 的 UDP fullcone 和更多增强很快就到！）
* 无须再担心证书验证被墙，OCSP stapling 已经上线!
* kirin 带来了一大波 脚本更新.[脚本在此](https://github.com/XTLS/Xray-install)
* 还有更多美味小樱桃！（不用问，更新品尝就对了）

## 2020.12.24

因为某些不可描述的原因，Xray 的文档网站已在发布日前偷跑上线。
网址为：[没错你正在看的就是](https://xtls.github.io)

大家可以查阅各种内容也欢迎纠错/提出建议（可发往文档 github 仓库的 issue 区）

文档网站需要不断完善和增加内容，以及完善设计。
因此更欢迎大家一起为文档建设添砖加瓦。
[文档的仓库](https://github.com/XTLS/XTLS.github.io)

仓库的 readme 中有简略教程说明如何帮助 xray 改进文档网站.
欢迎大家查看，纠错，修改，增加心得。

## 2020.12.23

Xray-core Shadowsocks UDP FullCone 测试版, [TG 群](https://t.me/projectXray)火热测试中

## 2020.12.21

* Project X 群人数 2000+
* 群消息(含游戏群) 日均破万

## 2020.12.18 [1.1.4](https://github.com/XTLS/Xray-core/releases/tag/v1.1.4)

* 更低的启动内占用和内存使用优化
* 随意定制的 TLS 提高你的 SSL 评级
* 支持 XTLS 入站的 Splice 以及支持 trojan 的 XTLS
* 还有在您路由器上使用的 Splice 最佳使用模式建议

## 2020.12.17

鉴于日益增长群人数和游戏需求, 开启了[TG 游戏群](https://t.me/joinchat/UO4NixbB_XDQJOUjS6mHEQ)

## 2020.12.15

[安装脚本 dev 分支](https://github.com/XTLS/Xray-install/tree/dev)开启, 持续更新功能中.

## 2020.12.11 [1.1.3](https://github.com/XTLS/Xray-core/releases/tag/v1.1.3)

* 完整版本的 REDIRECT 透明代理模式.
* 软路由 splice 流控模式的优化建议.

## 2020.12.06 [1.1.2](https://github.com/XTLS/Xray-core/releases/tag/v1.1.2)

* 流控增加 splice 模式, Linux 限定, 性能一骑绝尘.
* 增强了 API 兼容

## 2020.12.04

增加 splice 模式

## 2020.11.27

* Project X 的 GitHub 主仓库 Xray-core 已获 500+ stars
* 登上了 GitHub Trending
* Project X 群人数破千，频道订阅数 500+

## 2020.11.25 [1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0)

Xray 的第一个版本.

* 基于 v2ray-core 修改而来，改动较大
* 全面增强, 性能卓越, 完全兼容

## 2020.11.23

project X start

> \~~梦开始的时候~~

---

---
url: /about/sponsor.md
---
# 赞助 & 捐款 & NFTs

## Sponsors

[![Remnawave](https://github.com/user-attachments/assets/a22d34ae-01ee-441c-843a-85356748ed1e)](https://docs.rw)

[![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su)

[![BlancVPN](https://github.com/user-attachments/assets/9145ea7d-5da3-446e-8143-710dba4292c3)](https://blanc.link/ZtMXjZV)

[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)

## Donation & NFTs

### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)

* **TRX(Tron)/USDT/USDC: `TNrDh5VSfwd4RPrwsohr6poyNTfFefNYan`**
* **TON: `UQApeV-u2gm43aC1uP76xAC1m6vCylstaN1gpfBmre_5IyTH`**
* **BTC: `1JpqcziZZuqv3QQJhZGNGBVdCBrGgkL6cT`**
* **XMR: `4ABHQZ3yJZkBnLoqiKvb3f8eqUnX4iMPb6wdant5ZLGQELctcerceSGEfJnoCk6nnyRZm73wrwSgvZ2WmjYLng6R7sR67nq`**
* **SOL/USDT/USDC: `3x5NuXHzB5APG6vRinPZcsUv5ukWUY1tBGRSJiEJWtZa`**
* **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
* **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
* **VLESS NFT: https://opensea.io/collection/vless**
* **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
* **Related links: [VLESS Post-Quantum Encryption](https://github.com/XTLS/Xray-core/pull/5067), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113), [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)**
